2010-06-24 18 views
37

Veo una gran cantidad de código Java donde Android prefiere que los desarrolladores usen clases internas estáticas. Particularmente para patrones como el ViewHolder Pattern en Custom ListAdapters.¿Por qué Android prefiere las clases estáticas

No estoy seguro de cuáles son las diferencias entre las clases estáticas y las no estáticas. He leído sobre esto, pero no parece tener sentido cuando me preocupa el rendimiento o la huella de memoria.

Respuesta

62

No son sólo los desarrolladores de Android ...

Una clase interna no estática siempre mantiene una referencia implícita al objeto de cerramiento. Si no necesita esa referencia, todo lo que hace es costar la memoria. Considere esto:

class Outer { 
    class NonStaticInner {} 
    static class StaticInner {} 
    public List<Object> foo(){ 
     return Arrays.asList(
      new NonStaticInner(), 
      new StaticInner()); 
    } 
} 

Cuando se compila, lo que se obtiene será algo como esto:

class Outer { 
    Outer(){} 
    public List<Object> foo(){ 
     return Arrays.asList(
      new Outer$NonStaticInner(this), 
      new StaticInner()); 
    } 
} 
class Outer$NonStaticInner { 
    private final Outer this$0; 
    Outer$NonStaticInner(Outer enclosing) { this$0 = enclosing; } 
} 
class Outer$StaticInner { 
    Outer$StaticInner(){} 
} 
+2

Eso es cierto, pero hay más: a veces no tiene una referencia a la clase externa y aún desea crear una instancia de la parte interna (si está visible). – ognian

+4

+1 para la impresión de lo que hará el compilador desde la fuente de entrada – Seven

+0

No puede una clase interna no estática declarar explícitamente una referencia a la externa (solo necesita instanciarla manualmente), dando así lo mejor de ambos mundos: la referencia externa sin costos de memoria estática? Además, ¿no es una clase interna estática no reentrante, por lo tanto, es útil solo en el caso donde se requiere una sola instancia de la misma? – samosaris

14

Las clases internas estáticas (es decir, las clases declaradas dentro de otra clase con la palabra clave static) son bastante similares a las clases "normales" excepto que no contamina el espacio de nombre de su paquete. Esa es su (única) diferencia y beneficio, y creo que esa es la razón por la que lo ves en Android.

Usar clases internas estáticas cuando el objetivo de la clase es ajustar a la clase principal, pero no depende de sus instancias. Esto generalmente se considera una buena práctica.

+0

¿Puede entrar en más detalles con respecto a la clase interna estática y qué constituye una clase interna? Esto es intrigante – Mike

24

La principal diferencia entre las clases internas estáticas y no estáticas es que una clase interna no estática tiene acceso a otros miembros de la clase externa, incluso si son privadas. Las clases internas no estáticas son una "parte" de la clase externa. No puede crear ni puede existir sin una instancia de una clase externa. Una consecuencia de esto es que una instancia de una clase interna no estática se destruye cuando se destruye la instancia de la clase externa.

Las clases internas estáticas, por otro lado, son como las clases externas normales. Los viven y mueren solos. No necesita una instancia de la clase externa para que exista la clase interna. Eso significa que también tienen su propio ciclo de vida. Se destruyen cuando el recolector de basura decide destruirlos.

¿Cómo afecta esto la memoria y/o el rendimiento? Realmente no lo sé :)

+2

"¿Cómo afecta esto la memoria y/o el rendimiento?" Evitará pérdidas de memoria. Tener una clase interna estática le impide hacer una referencia a la Actividad/Fragmento, ya que la clase interna estática no puede acceder a la variable del miembro externo. Si tenemos una pérdida de memoria en la Actividad, y esa Actividad mantiene una referencia de muchos objetos pesados ​​(como Vistas), consumirá memoria innecesaria y ralentizará el rendimiento. – aldok

4

Si descompilar una clase interna (o verlo usando depurador) se puede ver que existe código generado para acceder a la instancia de la clase externa que se usó para crearlos. La sobrecarga para esto es más memoria para el puntero adicional, más CPU para la recolección de basura debido a un puntero adicional para probar, y si desea nit pick, tiempo de compilación más largo. Crear instancias de clases internas no estáticas es un poco más complicado porque necesita una instancia de la clase externa para crearlas.

Se puede controlar la visibilidad de las clases internas estáticas y no estáticas. Usualmente son privados si su implementación está fuertemente relacionada con los detalles internos de la clase externa, y el desarrollador no cree que el código pueda ser reutilizado. En este sentido, no son mejores que las funciones privadas. Las clases internas pueden ser públicas en casos como Map.Entry, donde la clase interna está fuertemente conectada a la interfaz expuesta por la clase, y el desarrollador no cree que Map.Entry se pueda usar sin algún tipo de Map. Ambos tipos tienen acceso a miembros privados de la clase externa y la clase externa tiene acceso a miembros privados de la clase interna.

Las instancias de clases internas estáticas y no estáticas se recogen como cualquier otra clase.No hay una conexión especial entre la colección de grabación de la clase externa y la colección de basura de la clase interna.

En el caso de la implementación de clases UI como swing o android, verá clases internas estáticas porque se tratan como funciones privadas. Estas clases no están desarrolladas para su reutilización fuera de la clase externa y están fuertemente conectadas a la implementación interna de la clase externa. No hay ninguna razón para exponerlos y asegurarse de que puedan funcionar en más casos que el contexto específico de los requisitos de la clase externa.

4

Una instancia de clase interna no estática contiene una referencia a la instancia de clase externa mientras que una instancia de clase interna estática no lo hace.

Esto es relevante para la huella de memoria de las aplicaciones, ya que la referencia oculta puede provocar pérdidas de memoria: el recolector de elementos no utilizados no puede recopilar la instancia de clase externa hasta que no haya más referencias. Además, la referencia adicional en sí misma necesita memoria, esto puede ser relevante si se usa una gran cantidad de instancias.

class Outer{ 
    class Inner{//Only works with non static inner class 
      public Outer getOuter(){return Outer.this;} 
    } 
} 

También es relevante para su uso, la referencia a la clase externa es un argumento ctor de la clase interna, para crear un nuevo objeto no estática clase interna que tiene que llamar a la ctor como un memberfunction en una instancia de la clase externa o desde dentro de una función miembro de la clase externa. Esto significa que no puede tener una instancia de la clase interna sin una instancia de la clase externa.

Outer.Inner in = new Outer().new Inner(); 
Cuestiones relacionadas