simple - super java
¿Alguna forma de no llamar al constructor de superclase en Java? (12)
Si tengo una clase:
class A {
public A() { }
}
y otro
class B extends A {
public B() { }
}
¿hay alguna forma de que BB()
no llame a AA()
?
Como lo señala otro póster, B no extiende A, por lo que no llamará al constructor de A de todos modos.
No hay manera de hacer esto en Java.
Probablemente pueda lograr de manera equivalente lo que quiere hacer de la siguiente manera:
a) en cada clase de su jerarquía, incluya un constructor con una firma única que llame al constructor de la superclase con sus argumentos. Por ejemplo, declare una clase "Noop" y un constructor que toma eso como un argumento:
public class NoOp {
}
public class class1 {
class1() {
System.out.println("class1() called");
}
class1(String x, String y) {
System.out.println("class1(String, String) called");
}
class1(NoOp x) {
System.out.println("class1(NoOp) called");
}
}
public class class2 extends class1 {
class2() {
System.out.println("class2() called");
}
class2(String x, String y) {
System.out.println("class2(String, String) called");
}
class2(NoOp x) {
super(x);
System.out.println("class2(NoOp) called");
}
}
public class class3 extends class2 {
class3() {
System.out.println("class3() called");
}
class3(String x, String y) {
super(new NoOp());
System.out.println("class3(String, String) called");
}
class3(NoOp x) {
super(x);
System.out.println("class3(NoOp) called");
}
public static void main(String args[]) {
class3 x = new class3("hello", "world");
}
}
Si ejecuta esto obtendrá la salida
class1(NoOp) called
class2(NoOp) called
class3(String, String) called
Entonces, efectivamente, has creado un constructor class3 que solo llama a los constructores que no hacen nada.
Cada objeto en java es una subclase de Objeto (objeto con una ''O'' mayúscula). cuando creas un objeto de una subclase, se invoca el constructor de la superclase. Incluso si su clase no está heredando ninguna otra clase, implícitamente está heredando Object, por lo que debe llamarse al constructor Object. Entonces se invoca super () para este propósito.
Cada superclase necesita construirse y no hay otra manera de llamar a un constructor.
Creo que la única forma de hacerlo es estropear el código de bytes.
No estoy seguro de si el Classloader o la JVM comprueban si se llama a super()
, pero, como escribió Bozho, probablemente terminaría con objetos inconsistentes cuando lo haga.
La deserialización de Java no llama al constructor, pero parece que se basa en algunos trucos internos de JVM.
Sin embargo, existe un marco que le permite hacerlo de manera portátil: Objenesis ( http://www.theserverside.com/discussions/thread/44297.html )
He visto esto recientemente en Spring, al usar proxies CGLIB, Spring crea dos instancias de clase, pero el constructor se llama solo una vez: https://.com/a/11583641/2557118
Este comportamiento se agrega en la primavera 4:
Las clases proxy basadas en CGLIB ya no requieren un constructor predeterminado. El soporte se proporciona a través de la biblioteca de objenesis que se vuelve a empaquetar en línea y se distribuye como parte de Spring Framework. Con esta estrategia, ya no se invoca a ningún constructor para las instancias de proxy.
Lo más cercano que puede lograr al comportamiento deseado es delegar la inicialización que normalmente se realiza en el constructor a un método de plantilla, que luego anula en su implementación de subclase. Por ejemplo:
public class A {
protected Writer writer;
public A() {
init();
}
protected void init() {
writer = new FileWriter(new File("foo.txt"));
}
}
public class B extends A {
protected void init() {
writer = new PaperbackWriter();
}
}
Sin embargo, como otras personas han notado, esto puede indicar un problema con su diseño y yo prefiero el enfoque de composición en este escenario; por ejemplo, en el código anterior, podría definir el constructor para aceptar una implementación de Writer
como parámetro.
No hay absolutamente ninguna manera de hacer esto en Java; rompería la especificación del lenguaje.
Ejecución de JLS 12 / 12.5 Creación de nuevas instancias de clase
Justo antes de que se devuelva una referencia al objeto recién creado como resultado, el constructor indicado se procesa para inicializar el nuevo objeto mediante el siguiente procedimiento:
- Asigna los argumentos para el constructor [...]
- Si este constructor comienza con una invocación explícita de otro constructor en la misma clase (usando
this
), entonces [...]- Este constructor no comienza con una invocación explícita de otro constructor en la misma clase (usando
this
). Si este constructor es para una clase que no seaObject
, entonces este constructor comenzará con una invocación explícita o implícita de un constructor de superclase (usandosuper
).- Ejecute los inicializadores de instancia y los inicializadores de variable de instancia para esta clase [...]
- Ejecutar el resto del cuerpo de este constructor [...]
No, no puedes hacerlo y ¿por qué querrías hacerlo de todos modos? Eso arruinaría tu modelo de objeto.
De todos modos, creo que si todavía desea hacerlo y luego tendría que manipular el código de byte generado ... hay un par de bibliotecas disponibles que facilitan la instrumentación del código de byte.
Fuertemente sugerir en contra de hacerlo ...
No, y si pudieras, tu objeto derivado realmente no sería el objeto del que se deriva ahora, ¿verdad? El principio is-a sería violado. Entonces, si realmente lo necesitas, entonces el polimorfismo no es lo que buscas.
Si no desea llamar al constructor de la superclase, hay algo más en su modelo de objeto.
Suponiendo que te refieres
class B extends A {
public B() { }
}
entonces seguro que puedes
class B extends A {
public B() {
this(abort());
}
private B(Void dummy) {
/* super(); */
}
private static Void abort() {
throw null;
}
}
No es muy útil. La interfaz [no es la palabra clave de Java] de la clase A
dice que necesita ejecutar su constructor para construirlo, no sin razón. La excepción es que las clases serializables se construyen sin llamar a los constructores de las clases serializables.
Tenía un requisito similar en el que necesitaba que mi clase infantil NO pasara por el constructor de la súper clase, y quería el resto de los beneficios de la súper clase. Como la súper clase también es mía, esto es lo que hice.
class SuperClass {
protected SuperClass() {
init();
}
// Added for classes (like ChildClassNew) who do not want the init to be invoked.
protected SuperClass(boolean doInit) {
if (doInit)
init();
}
//
}
class ChildClass1 extends SuperClass {
ChildClass1() {
// This calls default constructor of super class even without calling super() explicitly.
// ...
}
// ....
}
class ChildClass2 extends SuperClass {
ChildClass2() {
// This calls default constructor of super class even without calling super() explicitly.
// ...
}
// ....
}
class ChildClassNew extends SuperClass {
ChildClassNew() {
/*
* This is where I didn''t want the super class'' constructor to
* be invoked, because I didn''t want the SuperClass'' init() to be invoked.
* So I added overloaded the SuperClass'' constructor where it diesn;t call init().
* And call the overloaded SuperClass'' constructor from this new ChildClassNew.
*/
super(false);
//
// ...
}
// ....
}