Dado que tengo una clase Base que tiene un constructor de argumento único con un objeto TextBox como argumento. Si tengo una clase Simple de la siguiente forma:
public class Simple extends Base {
public Simple(){
TextBox t = new TextBox();
super(t);
//wouldn't it be nice if I could do things with t down here?
}
}
Recibiré un error que me dice que la llamada a super debe ser la primera llamada en un constructor. Sin embargo, por extraño que parezca, puedo hacer esto.
public class Simple extends Base {
public Simple(){
super(new TextBox());
}
}
¿Por qué se permite esto, pero no el primer ejemplo? Puedo entender la necesidad de configurar la subclase primero, y quizás no permitir que se creen instancias de variables de objeto antes de llamar al superconstructor. Pero es claramente una variable de método (local), entonces, ¿por qué no permitirlo?
¿Hay alguna forma de sortear esta limitación? ¿Existe una forma buena y segura de mantener variables para las cosas que podría construir ANTES de llamar a super pero DESPUÉS de haber ingresado al constructor? O, de forma más genérica, ¿permitir que el cálculo se realice antes de que se llame realmente a super, pero dentro del constructor?
Gracias.
Respuestas:
Sí, existe una solución para su caso simple. Puede crear un constructor privado que tome
TextBox
como argumento y llamarlo desde su constructor público.public class Simple extends Base { private Simple(TextBox t) { super(t); // continue doing stuff with t here } public Simple() { this(new TextBox()); } }
Para cosas más complicadas, necesita usar un método de fábrica o un método de fábrica estático.
fuente
super()
no aparezca después de nada más en el constructor. En casos extremos, incluso puede encadenar varios constructores privados juntos, aunque no puedo pensar en ninguna situación en la que necesite hacer eso, y si lo hiciera probablemente comenzaría a preguntarme si hay una mejor manera de lograr lo que estaba haciendo.super(this.getClass())
, pero como no se le permite hacer referenciathis
, ese código era ilegal, aunque parece perfectamente razonable que pueda conocer la clase real en este momento.getClass()
método como un caso especial en el JLS. Úselo en su<classname>.class
lugar.<classname>.class
no era una opción en este caso, porque estaba escribiendo una clase abstracta que la gente podía extender y necesitaba la clase real de la instancia.Tuve el mismo problema con el cálculo antes de la super llamada. A veces, desea verificar algunas condiciones antes de llamar
super()
. Por ejemplo, tienes una clase que usa muchos recursos cuando se crea. la subclase quiere algunos datos adicionales y es posible que desee verificarlos primero, antes de llamar al superconstructor. Hay una forma sencilla de solucionar este problema. puede parecer un poco extraño, pero funciona bien:Use un método estático privado dentro de su clase que devuelva el argumento del superconstructor y haga sus comprobaciones dentro:
public class Simple extends Base { public Simple(){ super(createTextBox()); } private static TextBox createTextBox() { TextBox t = new TextBox(); t.doSomething(); // ... or more return t; } }
fuente
Es requerido por el lenguaje para asegurar que la superclase se construya primero de manera confiable . En particular, "Si un constructor no invoca explícitamente a un constructor de superclase, el compilador de Java inserta automáticamente una llamada al constructor sin argumentos de la superclase".
En su ejemplo, la superclase puede depender del estado de
t
en el momento de la construcción. Siempre puedes pedir una copia más tarde.Hay una discusión extensa aquí y aquí .
fuente
Puede definir una lambda de proveedor estática que puede contener una lógica más complicada.
public class MyClass { private static Supplier<MyType> myTypeSupplier = () -> { return new MyType(); }; public MyClass() { super(clientConfig, myTypeSupplier.get()); } }
fuente
La razón por la que se permite el segundo ejemplo pero no el primero es más probable que mantenga el lenguaje ordenado y no introduzca reglas extrañas.
Permitir que se ejecute cualquier código antes de que se haya llamado a super sería peligroso, ya que podría meterse con cosas que deberían haberse inicializado pero aún no lo han sido. Básicamente, supongo que puedes hacer bastantes cosas en la llamada al super sí mismo (por ejemplo, llamar a un método estático para calcular algunas cosas que deben ir al constructor), pero nunca podrás usar nada del no -objeto aún completamente construido, lo cual es bueno.
fuente
Así es como funciona Java :-) Hay razones técnicas por las que se eligió de esta manera. De hecho, podría ser extraño que no pueda hacer cálculos en locales antes de llamar a super, pero en Java el objeto primero debe asignarse y, por lo tanto, debe ir hasta Object para que todos los campos se inicialicen correctamente antes de que pueda modificar accidentalmente ellos.
En su caso, la mayoría de las veces existe un getter que le permite acceder al parámetro que le dio a super (). Entonces usarías esto:
fuente
final
método que no sea de instancia y que una subclase anule. La anulación no verá los campos inicializados de la subclase (¡inclusofinal
aquellos con inicializadores de instancia!). Algunas herramientas de análisis estático advertirán acertadamente sobre esta situación.Esta es mi solución que permite crear un objeto adicional, modificarlo sin crear clases, campos, métodos adicionales, etc.
class TextBox { } class Base { public Base(TextBox textBox) { } } public class Simple extends Base { public Simple() { super(((Supplier<TextBox>) () -> { var textBox = new TextBox(); //some logic with text box return textBox; }).get()); } }
fuente