Pregunta

Supongamos que tengo una clase base B y una clase derivada D. Deseo tener un método foo () dentro de mi clase base que devuelva un nuevo objeto del tipo que sea la instancia. Entonces, por ejemplo, si llamo a B.foo (), devuelve un objeto de tipo B, mientras que si llamo a D.foo (), devuelve un objeto de tipo D; Mientras tanto, la implementación reside únicamente en la clase B base.

¿Es esto posible?

¿Fue útil?

Solución

No lo hagas. Hacer que " foo " resumen del método.

abstract class B {
    public abstract B foo();
}

O reciba una fábrica abstracta a través del constructor de clase base:

abstract class B {
    private final BFactory factory;
    protected B(BFactory factory) {
        this.factory = factory;
    }
    public B foo() {
        return factory.create();
    }
}
interface BFactory {
    B create();
}

Agregue tipos de retorno covariantes y genéricos al gusto.

Otros consejos

Siempre que cada clase tenga un constructor predeterminado:

    public B instance() throws Exception {
        return getClass().newInstance();
    }

Bueno, podría estar fuera pero supondría que desde " esto " siempre se refiere al objeto actual, podrías hacer algo como

public B foo() {
    return this.getClass().newInstance();
}

o algo por el estilo? Si creas una instancia de D y luego llamas a d.foo () deberías obtener una instancia de D devuelta como B. Podrías devolverlo como un Objeto simple pero deberías ser lo más específico posible en esta instancia, creo.

Además del hecho de que creo que probablemente haya un defecto de diseño si quieres lograr esto, puedes probar el siguiente enfoque.

En su pregunta, está usando métodos estáticos (clase), B.foo (), D.foo (), esto no se puede lograr usando herencia porque los métodos estáticos no tienen una naturaleza dinámica, no participan. en el sistema de búsqueda Así que no tienes suficiente información de tipo.

Si está utilizando una función miembro foo (), podría tener la siguiente construcción:

public class B {
    public B foo()
    throws IllegalAccessException, InstantiationException {
        return this.getClass().newInstance();
    }
}

public class D  extends B{    
}

public class Test {
    public static final void main(String[] args)
    {
        try {
            System.out.println((new B()).foo());
            System.out.println((new D()).foo());
        } catch (IllegalAccessException e) {
            e.printStackTrace();  
        } catch (InstantiationException e) {
            e.printStackTrace();  
        }
    }
}

Como dicen las otras respuestas, puede usar getClass (). newInstance () si hay un constructor sin argumentos en cada subclase (asegúrese de capturar InstantiationException e IllegalAccessException).

Si alguno de los constructores requiere argumentos, puede usar la reflexión o (preferible en mi opinión) definir un método como getNewInstance () que puede anular en la subclase solo si es necesario.

por ejemplo

Thing foo() {
    Thing th = getNewInstance();
    // do some stuff with th
    return th;
}

Thing getNewInstance() {
    return getClass().newInstance();
}

Luego, getNewInstance () se puede anular solo si realmente lo necesitas, para las subclases que no tienen el constructor predeterminado.

Thing getNewInstance() {
    return new BigThing(10, ThingSize.METRES);
}

Creo que esto podría hacerse usando la reflexión, es decir, en su superclase tiene:

public ClassName getFoo() throws InstantiationException, IllegalAccessException
{
    return getClass().newInstance();
}

Donde ClassName es el nombre de su clase base.

Sin embargo, tendrás que lanzarlo donde quieras usarlo ... ¡No estoy seguro de que sea realmente una gran solución!

Editar: los métodos de tipo newInstance () suelen ser estáticos y, por supuesto, no tendrá una idea de qué tipo de subclase tiene con un método estático.

No creo que haya ninguna forma de obtener un método static para crear (dinámicamente) una instancia de una subclase.

@Rik

Bueno, el verdadero problema es que tengo una clase base abstracta. Y Thing tiene un método llamado getNextThing () que devuelve una nueva instancia de Thing.

Luego, tengo varias subclases como BigThing, LittleThing, SomethingThing y no quiero seguir reescribiendo el método getNextThing () para cada una de ellas. Parece un desperdicio, ya que todos hacen lo mismo ... cosa.

¿Soy incorrecto en mi enfoque?

No estoy seguro de por qué estás tratando de hacer lo que realmente estás tratando de hacer. Proporcionar más contexto podría permitirnos brindarle más ayuda.

public class B <X extends B>{

public X foo() throws InstantiationException, IllegalAccessException{
    return (X)this.getClass().newInstance();
}
}        
public class C extends B<C>{        

}

Permíteme ofrecerte este consejo. La forma en que se enseñan las clases de CS es que los profesores están enamorados de la herencia, pero no han descubierto la composición. Creo que lo que podrías estar buscando es composición. Entonces, en lugar de llamar a getNextThing en el Thing en sí, tal vez debería pensar en hacer que Thing implemente Iterable

Esto significa que solo tendrá que escribir un Iterador que puede encapsular la lógica de obtener lo siguiente, ya que no parece encajar en su modelo de herencia. Otra ventaja de esto es que obtienes algunos dispositivos sintácticos agradables (mejorados para la comprensión de bucles).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top