2010-07-02 17 views
54

Tengo una clase abstracta A que define métodos abstractos. Esto significa que, para que una clase sea instanciable, todo el método abstracto debe implementarse.¿Cómo puedo forzar que un Constructor se defina en todas las subclases de mi clase abstracta?

Me gustaría que todas mis subclases implementaran un constructor con 2 entradas como parámetros.

Al declarar un constructor se frustra mi propósito, ya que quiero que el constructor esté definido en subclases y no sé nada sobre la implementación. Además, no puedo declarar que un constructor sea abstracto;

¿Hay alguna manera de hacerlo?

ejemplo de lo que quiero:

Digamos que estoy definiendo la API de una clase Matrix. En mi problema, Matrix no puede cambiar sus dimensiones.

Para que se cree una matriz, necesito proporcionar su tamaño.

Por lo tanto, quiero que todos mis implementadores proporcionen al constructor el tamaño como parámetro. Este constructor está motivado por el problema, no por una preocupación de implementación. La implementación puede hacer lo que quiera con estos, siempre que se mantenga toda la semántica de los métodos.

Digamos que quiero proporcionar una implementación básica del método invert() en mi clase abstracta. Este método creará una nueva matriz con this dimensiones invertidas. Más específicamente, como se define en la clase abstracta, creará una nueva instancia de la misma clase que this, usando un constructor que toma dos ints. Como no conoce la instancia, utilizará la reflexión (getDefinedConstructor) y quiero una forma de garantizar que la obtendré y que será significativa para la implementación.

+0

Una solución elegante se puede encontrar aquí: http://stackoverflow.com/questions/6028526/java-force-an-extinging-class –

Respuesta

48

No puede forzar una firma particular de constructor en su subclase, pero puede obligarlo a pasar por un constructor en su clase abstracta tomando dos enteros. Las subclases podrían llamar a ese constructor desde un constructor sin parámetros, pasando por las constantes, por ejemplo. Sin embargo, eso es lo más cercano que puedes venir.

Además, como dices, no sabes nada sobre la implementación, así que ¿cómo sabes que es apropiado que tengan un constructor que requiera dos enteros? ¿Qué pasa si uno de ellos también necesita una Cadena? O posiblemente tiene sentido que use una constante para uno de esos enteros.

¿Cuál es la imagen más grande aquí? ¿por qué quieres forzar una firma particular de constructor en tus subclases? (Como ya he dicho, no se puede hacer realidad esto, pero si se le explica por qué lo quiere, una solución podría presentarse a sí misma.)

Una opción es tener una interfaz independiente para una fábrica:

interface MyClassFactory 
{ 
    MyClass newInstance(int x, int y); 
} 

Luego, cada una de las subclases concretas de MyClass también necesitaría una fábrica que supiera cómo crear una instancia con dos enteros. Sin embargo, no es terriblemente conveniente, y aún tendría que crear instancias de las propias fábricas. De nuevo, ¿cuál es la situación real aquí?

+2

Digamos que estoy definiendo la API de una clase Matrix. En mi problema, Matrix no puede cambiar sus dimensiones. Para crear una matriz, necesito proporcionar su tamaño. Por lo tanto, quiero que todos mis implementadores proporcionen al constructor el tamaño como parámetro. Este constructor está motivado por el problema, no por una preocupación de implementación. La implementación puede hacer lo que quiera con estos, siempre que se mantenga toda la semántica de los métodos. – dodecaplex

+0

@dodecaplex: Pero, ¿y si quieres crear una implementación 'FixedSizeMatrix' que sea * siempre * 10 x 10? No puede * llamar * constructores polimórficamente de todos modos, entonces ¿por qué está tratando de restringir la implementación? –

+1

Bueno, entonces la implementación no se ajustará a mi API ... Si tiene buenas razones para hacerlo, proporcionará un constructor de cero arg y un constructor de 2 args que generará una excepción si los argumentos no son 10x10. Esto significa que aún podré crear una Matriz vacía de la misma implementación y del mismo tamaño (sin conocer la implementación efectiva), pero obtendré una excepción si intento usar esta implementación para matrices no 10x10 . – dodecaplex

2

Si necesita definir en su interfaz la representación interna que utilizarán las clases de implementación, entonces simplemente lo está haciendo mal. Por favor, lee acerca de encapsulation y data abstraction.

Si su implementación abstracta se basa en ciertos detalles de implementación, entonces pertenecen a esa clase abstracta. Es decir, la clase abstracta debe definir un constructor que le permita inicializar el estado interno necesario para permitir que los métodos abstractos funcionen.

En general, los constructores están destinados a crear una instancia de una clase proporcionando algunos detalles del estado inicial de esa instancia de objeto. Esto no significa que la instancia que se está construyendo debe copiar una referencia a cada argumento individual, como suele ser el caso en la mayoría del software que veo. Por lo tanto, incluso si Java ofreciera una construcción para forzar la implementación de ciertas firmas de Constructor en subclases, esas subclases podrían descartar fácilmente los argumentos.

+1

Bueno, si estoy definiendo un Punto en un espacio de n dimensiones, sé con certeza que el punto no puede existir en un espacio de dimensión m (m! = N). Por lo tanto, me parece que la dimensión del espacio es un atributo INHERENTE del Punto, independientemente de la implementación del punto. Por lo que puedo decir, esto es encapsulación y abstracción de datos. Si la dimensión se conoce NECESARIAMENTE para cada instancia que se creará, parece natural REQUERIR que todas las implementaciones proporcionen un constructor que tome este argumento. – dodecaplex

+0

@dodecaplex, Ahh, un punto, gran ejemplo. Supongamos que estás hablando de un punto bidimensional. Bueno, claro, puedo usar un valor xey para representarlo. O bien, puedo usar un valor de radianes/grados y un valor de distancia para representarlo. Solo un ejemplo. Podría haber otras formas. Solo piensas que sabes cómo se puede implementar. De todos modos, por definición, si está tratando de hacer cumplir una representación, no está utilizando la abstracción de datos. –

3

Puede intentar algo como a continuación. El constructor arrojará una excepción si la clase implementadora no tiene un constructor con los argumentos apropiados.

Esto es tonto. Compare OK y Bad. Ambas clases son iguales, excepto que OK cumple con sus requisitos y, por lo tanto, aprueba los controles de tiempo de ejecución. Por lo tanto, hacer cumplir el requisito promueve el trabajo ocupado contraproducente.

Una mejor solución sería algún tipo de fábrica.

abstract class RequiresConstructor 
{ 
    RequiresConstructor(int x, int y) throws NoSuchMethodException 
    { 
    super(); 
    System.out.println(this.getClass().getName()) ; 
    this.getClass(). getConstructor (int.class , int.class) ; 
    } 

    public static void main(String[] args) throws NoSuchMethodException 
    { 
    Good good = new Good (0, 0); 
    OK ok = new OK(); 
    Bad bad = new Bad(); 
    } 
} 

class Good extends RequiresConstructor 
{ 
    public Good(int x, int y) throws NoSuchMethodException 
    { 
    super(x, y) ; 
    } 
} 

class OK extends RequiresConstructor 
{ 
    public OK(int x, int y) throws NoSuchMethodException 
    { 
    super(x, y) ; 
    throw new NoSuchMethodException() ; 
    } 

    public OK() throws NoSuchMethodException 
    { 
    super(0, 0) ; 
    } 
} 

class Bad extends RequiresConstructor 
{ 
    public Bad() throws NoSuchMethodException 
    { 
    super(0, 0) ; 
    } 
} 
0

Tenga la clase abstracta tiene un método abstracto que toma lo que usted tendría para los parámetros. Por ejemplo:

public abstract void setSize(int rows,int columns); 
2

Un poco tarde, pero ...

Basta con crear un constructor por defecto en su clase, que siempre se llama como súper constructor. En este constructor predeterminado puede verificar todos los constructores definidos con un reflejo en su propio objeto de clase (que entonces no es la superclase abstracta sino la subclase concreta). Si falta el constructor que desea implementar, ejecute una excepción en tiempo de ejecución.

No soy un gran amigo de la reflexión, ya que tiene el sabor de la piratería a través de la puerta trasera, pero a veces ayuda ...

Tener un vistazo a este ejemplo:

import java.lang.reflect.Constructor; 

public abstract class Gaga { 
    public Gaga() { 
    boolean found = false; 
    try { 
     Constructor<?>[] constructors = getClass().getConstructors(); 
     for (Constructor<?> c : constructors) { 
     if (c.getParameterTypes().length==2) { 
      Class<?> class0 = c.getParameterTypes()[0]; 
      Class<?> class1 = c.getParameterTypes()[1]; 
      if ((class0.getName().equals("int") || class0.isAssignableFrom(Integer.class)) 
       && (class1.getName().equals("int") || class1.isAssignableFrom(Integer.class))) 
      found = true; 
     } 
     } 
    } catch (SecurityException e) 
    { 
     found = false; 
    } 

    if (!found) 
     throw new RuntimeException("Each subclass of Gaga has to implement a constructor with two integers as parameter."); 

    //... 
    } 

} 

y una clase de prueba:

public class Test { 
    private class Gaga1 extends Gaga { 
    public Gaga1() { this(0, 0); } 
    public Gaga1(int x, Integer y) { } 
    } 

    private class Gaga2 extends Gaga { 

    } 

    public static void main(String[] args) 
    { 
    new Gaga1(); 
    new Gaga1(1, 5); 
    new Gaga2(); 
    System.exit(0); 
    } 
} 

En la función principal se crearán los objetos de Gaga1, sino la creación de Gaga2 arrojarán una excepción de tiempo de ejecución.

Pero no puede estar seguro de que se llame a este constructor; ni siquiera puede asegurarse de que está haciendo lo que desea.

Esta prueba solo es útil si está trabajando con reflexión.

Cuestiones relacionadas