2009-04-27 16 views
5

Tengo una clase Set (Esto es J2ME, por lo que tengo acceso limitado a la API estándar, solo para explicar mi aparente reinvención de la rueda). Estoy usando mi clase set para crear conjuntos de cosas constantes en clases y subclases. De alguna manera se parece a esto ...¿Puedo garantizar el orden en que se ejecutan los inicializadores estáticos en Java?

class ParentClass 
{ 
    protected final static Set THE_SET = new Set() {{ 
     add("one"); 
     add("two"); 
     add("three"); 
    }}; 
} 


class SubClass extends ParentClass 
{ 
    protected final static Set THE_SET = new Set() {{ 
     add("four"); 
     add("five"); 
     add("six"); 
     union(ParentClass.THE_SET); /* [1] */ 
    }}; 
} 

Todo parece bien, excepto la línea en [1] provoca una excepción de puntero nulo. Presumiblemente esto significa que el inicializador estático en la subclase se está ejecutando antes que el de la clase padre. Esto me sorprendió porque habría pensado que primero ejecutaría los bloques estáticos en cualquier importación nueva, antes de ejecutar cualquiera en la subclase instatiated.

¿Estoy en lo correcto en esta suposición? ¿Hay alguna forma de controlar o evitar este comportamiento?

Actualización:

cosas son aún más extraño. Probé este lugar (Tenga en cuenta el 'nuevo ParentClass()' línea):

class ParentClass 
{ 
    public ParentClass() 
    { 
     System.out.println(THE_SET); 
    } 

    protected final static Set THE_SET = new Set() {{ 
     add("one"); 
     add("two"); 
     add("three"); 
    }}; 
} 


class SubClass extends ParentClass 
{ 
    protected final static Set THE_SET = new Set() {{ 
     System.out.println("a"); 
     new ParentClass(); 
     System.out.println("b"); 
     add("four"); 
     System.out.println("c"); 
     add("five"); 
     System.out.println("d"); 
     add("six"); 
     System.out.println("e"); 
     union(ParentClass.THE_SET); /* [1] */ 
     System.out.println("f"); 
    }}; 
} 

y la salida es extraño:

a 
["one", "two", "three"] 
b 
c 
d 
e 
Exception in thread "main" java.lang.ExceptionInInitializerError 
Caused by: java.lang.NullPointerException 

Así ParentClass es inicializado, pero la subclase no tienen acceso a en su inicializador estático.

Respuesta

7

¿Es esto lo que estás tratando de lograr? ¿O necesitas una implementación local de la interfaz Set?

class ParentClass 
{ 
    protected final static Set THE_SET; 

    static { 
     THE_SET = new HashSet(); 
     THE_SET.add("one"); 
     THE_SET.add("two"); 
     THE_SET.add("three"); 
    } 
} 


class SubClass extends ParentClass 
{ 
    protected final static Set THE_SECOND_SET; 

    static { 
     THE_SECOND_SET = new HashSet(); 
     THE_SECOND_SET.add("four"); 
     THE_SECOND_SET.add("five"); 
     THE_SECOND_SET.add("six"); 
     union(ParentClass.THE_SET); /* [1] */ 
    } 
} 
+0

Sí ... eso hace lo mismo. – izb

+1

Creo que Elijah lo hizo bien. Esto no es un problema de orden de iniciación, sino más bien una especie de choque de nombres. – boutta

+0

En mi código real, en realidad tengo 3 clases donde A <-B <-C. La excepción fue en B. Resulta interesante que cada clase con su propio nombre de conjunto mueva la excepción a C, pero no resuelve el problema. Esto simplemente implica que el orden realmente es impredecible. – izb

3

No hay garantía para el orden del inicializador estático entre las clases. Dentro de una clase, se ejecutan en el orden del código fuente.

Si lo piensas bien, en realidad no podría haber una orden entre las clases, ya que no controlas cuándo se cargan las clases; puede cargar dinámicamente una clase, o la JVM puede optimizar el orden de carga.

+0

Si la clase A extiende la clase B, ¿no tendría que cargarse completamente la clase A antes de poder cargar la clase B? –

+0

Hubiera pensado que sería al revés. Seguramente A depende de que B esté allí, entonces B debe inicializarse primero. – izb

+0

Pero para obtener (o saber acerca de B) tiene que pasar primero por el código en A. Lo que significa que un código de bloque estático pasará antes que el código en B, yo diría. –

2

Incluso si no tiene el extends ParentClass allí, usar ParentClass debe hacer que se inicialice.

Donde las cosas se vuelven complicadas es cuando tienes ciclos. Con un ciclo, es posible acceder a una clase antes de que se haya inicializado por completo. Como Java es un sistema multiproceso, también puede tener problemas con interbloqueos y carreras.

Puede ser que su implementación de Java ME tenga fallas (algo que no se conoce). Parte tus ParentClass referencias completas tu ChildClass. O tal vez hay alguna otra aplicación/error de la biblioteca.

En una nota relacionada, si no lo hace -target 1.4 o posterior, inner classes 'outer esto no se inicializa tan pronto como podría esperar. Tal como se presenta, su código usa (técnicamente) clases internas en un contexto estático, por lo que no debería ser un problema.

También es interesante señalar que la inicialización estática es un poco confusa en situaciones como esta, porque en realidad tiene cuatro clases allí.

0

Teniendo en cuenta la línea de salida de [ "uno", "dos", "tres"], que es básicamente imposible que ParentClass.THE_SET nunca se ha inicializado.

Por supuesto, es posible que no haya solo un cargador de clases involucrado, pero sin duda sería útil ver el método y el número de línea donde ocurre el puntero nulo.

+0

J2ME no permite cargadores de clases personalizados ... es bastante primitivo. Además, la falta de 'f' en el resultado implica que el error ocurrió en la línea de unión() que lo precede. – izb

2

Simplemente deje de abusar del concepto de clases anónimas, por ejemplo, inicialización (la llamada "expresión idiomática doble").

+0

Sabes que siempre pensé que esto era solo una sintaxis útil ... No tenía idea de que en realidad era una clase anónima. – izb

+0

Esa es la razón más grande por la que odio que las personas lo llamen un "modismo" y le den un nombre; lo hace sonar como una característica separada del lenguaje y oscurece lo que realmente sucede. –

-1

Creo que algo similar fue escrito en el libro de acertijos de Java o en el video de YouTube de Google sobre trucos de Java.

Cuestiones relacionadas