2010-02-05 23 views
79

¿Por qué las clases Java no pueden tener campos abstractos como pueden tener métodos abstractos?¿Por qué no campos abstractos?

Por ejemplo: Tengo dos clases que extienden la misma clase base abstracta. Estas dos clases tienen cada una un método que es idéntico, excepto por una constante de cadena, que resulta ser un mensaje de error, dentro de ellas. Si los campos pudieran ser abstractos, podría hacer este resumen constante e incorporar el método a la clase base. En cambio, tengo que crear un método abstracto, llamado getErrMsg() en este caso, que devuelve el String, anula este método en las dos clases derivadas, y luego puedo extraer el método (que ahora llama al método abstracto).

¿Por qué no podría simplemente hacer el resumen de campo para empezar? ¿Podría Java haberse diseñado para permitir esto?

+3

Parece que podría haber esquivado este problema al hacer que el campo no sea constante y simplemente suministrar el valor a través del constructor, terminando con 2 instancias de una clase en lugar de 2 clases. – Nate

+4

Al hacer un resumen de campos en una superclase, tiene que especificar que cada subclase debe tener este campo, por lo que no es diferente de un campo no abstracto. –

+0

@peter, no estoy seguro de estar siguiendo su punto. si se especificó una constante no abstracta en la clase abstracta, entonces su valor también es constante en todas las subclases. si fuera abstracto, su valor debería ser implementado/suministrado por cada subclase. entonces, no sería lo mismo en absoluto. – liltitus27

Respuesta

80

Usted puede hacer lo que usted describe por tener un campo final en su clase abstracta que se inicializa en su constructor (código no probado):

abstract class Base { 

    final String errMsg; 

    Base(String msg) { 
     errMsg = msg; 
    } 

    abstract String doSomething(); 
} 

class Sub extends Base { 

    Sub() { 
     super("Sub message"); 
    } 

    String doSomething() { 

     return errMsg + " from something"; 
    } 
} 

Si su clase hija "se olvida" para inicializar el final a través de la super constructor el compilador dará una advertencia un error, al igual que cuando un método abstracto no se implementa.

+0

No tengo un editor para probarlo aquí, pero esto era lo que iba a publicar también .. – Tim

+4

"el compilador dará una advertencia". En realidad, el constructor Child intentaría usar un constructor noargs inexistente y es un error de compilación (no una advertencia). –

+0

Y agregar al comentario de @Stephen C, "como cuando un método abstracto no se implementa" también es un error, no una advertencia. –

3

Obviamente podría han sido diseñados para permitir esto, pero bajo las cubiertas todavía tendría que hacer el envío dinámico, y por lo tanto una llamada a un método. El diseño de Java (al menos en los primeros días) fue, hasta cierto punto, un intento de ser minimalista. Es decir, los diseñadores trataron de evitar agregar nuevas características si pudieran ser fácilmente simuladas por otras características que ya estaban en el idioma.

+0

@Laurence - es * no * obvio para mí que los campos abstractos podrían haberse incluido. Es probable que algo así tenga un impacto profundo y fundamental en el sistema de tipos y en la usabilidad del lenguaje. (De la misma manera que la herencia múltiple trae problemas profundos ... que pueden morder al incauto programador C++). –

5

No veo ningún punto en eso. Puede mover la función a la clase abstracta y simplemente anular algún campo protegido. No sé si esto funciona con constantes pero el efecto es el mismo:

public abstract class Abstract { 
    protected String errorMsg = ""; 

    public String getErrMsg() { 
     return this.errorMsg; 
    } 
} 

public class Foo extends Abstract { 
    public Foo() { 
     this.errorMsg = "Foo"; 
    } 

} 

public class Bar extends Abstract { 
    public Bar() { 
     this.errorMsg = "Bar"; 
    } 
} 

Así que su punto es que desea aplicar a la aplicación/primordial/lo que sea de errorMsg en las subclases? Pensé que solo querías tener el método en la clase base y no sabías cómo lidiar con el campo entonces.

+0

Eso es exactamente lo que estaba escribiendo. Solo está el rápido y el muerto por aquí ... Excepto que escribiste mal "Foo" en el errorMsg. :) –

+0

Gracias por señalar :) –

+3

Bueno, podría hacerlo, por supuesto. Y lo había pensado. Pero ¿por qué tengo que establecer un valor en la clase base cuando sé con certeza que voy a anular ese valor en cada clase derivada? Su argumento también podría usarse para argumentar que tampoco tiene ningún valor permitir métodos abstractos. –

0

Al leer su título, pensé que se refería a los miembros de la instancia abstracta; y no pude ver mucho uso para ellos. Pero los miembros estáticos abstractos son otra cosa completamente diferente.

menudo he deseado que podría declarar un método como el siguiente en Java:

public abstract class MyClass { 

    public static abstract MyClass createInstance(); 

    // more stuff... 

} 

Básicamente, me gustaría insistir en que las implementaciones concretas de mi clase padre proporcionan un método de fábrica estática con una firma específica . Esto me permitiría obtener una referencia a una clase concreta con Class.forName() y estar seguro de que podría construir una en una convención de mi elección.

+1

¡Sí, bueno ... esto probablemente significa que estás usando la reflexión mucho! –

+0

Suena como un extraño hábito de programación para mí. Class.forName (..) huele como un defecto de diseño. – whiskeysierra

+0

En estos días, básicamente siempre usamos Spring DI para lograr este tipo de cosas; pero antes de que ese marco se volviera conocido y popular, deberíamos especificar clases de implementación en propiedades o archivos XML, luego usar Class.forName() para cargarlos. –

0

Otra opción es definir el campo como público (final, si lo desea) en la clase base, y luego inicializar ese campo en el constructor de la clase base, dependiendo de qué subclase se esté utilizando actualmente. Es un poco sospechoso, ya que introduce una dependencia circular. Pero, al menos, no es una dependencia que puede cambiar alguna vez; es decir, la subclase existirá o no existirá, pero los métodos o campos de la subclase no pueden influir en el valor de field.

public abstract class Base { 
    public final int field; 
    public Base() { 
    if (this instanceof SubClassOne) { 
     field = 1; 
    } else if (this instanceof SubClassTwo) { 
     field = 2; 
    } else { 
     // assertion, thrown exception, set to -1, whatever you want to do 
     // to trigger an error 
     field = -1; 
    } 
    } 
} 
0

Se podría definir un constructor con parámetros en la clase abstracta.

De esta manera las subclases se ven obligados a definir sus propios constructores y llamar al constructor de la superclase, algo así como lo siguiente:

public abstract class TransactionManager { 
    private String separator; 

    public TransactionManager(String separator) { 
     this.separator = separator; 
    } 
} 

Entonces su clase concreta se vería así:

public class TransactionManagerFS extends TransactionManager{ 

    // The IDE forces you to implement constructor. 
    public TransactionManagerFS(String separator) { 
     super(separator); 
    } 
} 

Comprobar completa ejemplo desde here.

Cuestiones relacionadas