2010-04-03 36 views
15

¿Por qué no puedo convertir una instancia de clase base en una clase derivada?Java; clase base de conversión a la clase derivada

Por ejemplo, si tengo una clase B que amplía una clase C, ¿por qué no puedo hacer esto?

B b=(B)(new C()); 

¿o este?

C c=new C(); 
B b=(B)c; 

bien déjame ser más específico en cuanto a lo que estoy tratando de hacer. Aquí es lo que tengo:

public class Base(){ 
    protected BaseNode n; 
    public void foo(BaseNode x){ 
     n.foo(x); 
    } 
} 


public class BaseNode(){ 
    public void foo(BaseNode x){...} 
} 

Ahora quiero crear un nuevo conjunto de clases que se extienden base y Basenode, así:

public class Derived extends Base(){ 
    public void bar(DerivedNode x){ 
     n.bar(x);//problem is here - n doesn't have bar 
    } 
} 

public class DerivedNode extends BaseNode(){ 
    public void bar(BaseNode){ 
     ... 
    } 
} 

Así que, esencialmente, quiero añadir nueva funcionalidad a la base y BaseNode al extenderlos a ambos y agregarles una función a ambos. Además, Base y BaseNode deberían poder usarse solos.

Realmente me gustaría hacer esto sin genéricos si es posible.


Muy bien, así que terminé descifrándolo, en parte gracias a la respuesta de Maruice Perry.

En mi constructor para Base, n se instancia como BaseNode. Todo lo que tuve que hacer fue volver a crear una instancia de n como DerivedNode en mi clase derivada en el constructor, y funciona perfectamente.

+0

En primer lugar, debe hacer BaseNode n; protegido para que Derived pueda verlo. – Joel

+0

Woops (lo cambió). – Cam

+0

Es decir, ¿no hay forma de hacerlo sin los genéricos? Como dije, quiero que las clases base sean utilizables tal como están; agregar genéricos hará que sean más engorrosos de usar y elimina la encapsulación de la clase BaseNode que se supone que está encapsulada por Base. – Cam

Respuesta

6

Es necesario utilizar el instanceof palabra clave para comprobar el tipo de objeto referenciado por n y encasillado el objeto y llama a la bar() método. Pedido Derived.bar() método de abajo

public class Test{ 
    public static void main(String[] args){ 
     DerivedNode dn = new DerivedNode(); 
     Derived d = new Derived(dn); 
     d.bar(dn); 
    } 
} 

class Base{ 
    protected BaseNode n; 
    public Base(BaseNode _n){ 
     this.n = _n; 
    } 

    public void foo(BaseNode x){ 
     n.foo(x); 
    } 
} 


class BaseNode{ 
    public void foo(BaseNode x){ 
     System.out.println("BaseNode foo"); 
    } 
} 

class Derived extends Base{ 
    public Derived(BaseNode n){ 
     super(n); 
    } 

    public void bar(DerivedNode x){ 
     if(n instanceof DerivedNode){ 
      // Type cast to DerivedNode to access bar 
      ((DerivedNode)n).bar(x); 
     } 
     else { 
      // Throw exception or what ever 
      throw new RuntimeException("Invalid Object Type"); 
     } 
    } 
} 

class DerivedNode extends BaseNode{ 
    public void bar(BaseNode b){ 
     System.out.println("DerivedNode bar"); 
    } 
} 
19

porque si B se extiende C, significa que B es un C y C no es una B.

replantearse lo que está tratando de hacer.

+0

He editado mi publicación y he agregado lo que estoy tratando de hacer. ¿Algunas ideas? – Cam

2

No se puede hacer eso porque C no tiene por qué poner en práctica las conductas que creó cuando la extendió en B.

Así, por ejemplo C tiene un método foo(). Entonces sabrá que puede llamar al foo() en una B, ya que B extiende C, por lo que puede emitir en consecuencia una golosina B como si fuera una C con (C)(new B()).

Sin embargo, si B tiene un método bar(), nada en la relación de la subclase indica que también puede llamar al bar() en C. Por lo tanto, no puedes tratar una C como si fuera una B, y entonces no puedes lanzar.

0

Porque si B extends C, entonces B podría tener cosas que no se encuentra en C (como variables de instancia que inicializar en el constructor que no están en nuevo C())

3

Puede crear un constructor para B que toma C como un parámetro. Consulte this post para obtener ideas para hacer lo que está tratando de hacer.

16

Las respuestas existentes están bien en términos de un argumento abstracto, pero me gustaría hacer una más concreta. Supongamos que podría hacer eso.A continuación, el código tendría que compilar y ejecutar:

// Hypothetical code 
Object object = new Object(); 
InputStream stream = (InputStream) object; // No exception allowed? 
int firstByte = stream.read(); 

exactamente dónde sería la puesta en práctica del método read viene? Es abstracto en InputStream. ¿De dónde obtendría los datos? Simplemente no es apropiado para tratar un número java.lang.Object como InputStream. Es mucho mejor para el reparto lanzar una excepción.

En mi experiencia, es complicado obtener "jerarquías de clases paralelas" como la que está describiendo para funcionar. Usted puede encontrar que los medicamentos genéricos ayudan, pero puede ponerse peludo muy rápidamente.

+0

Gracias Jon. Después de pensar un poco más, esto tiene sentido. Otra explicación podría ser esta: ya podemos convertir una clase derivada a una clase base, lo que significa que podemos convertir cualquier cosa en un Objeto. Sin embargo, si pudiéramos convertir una clase base en una clase derivada, podríamos efectivamente lanzar un Objeto como cualquier cosa. Si pudiéramos hacer ambas cosas, cualquier clase podría ser transferida a cualquier otra clase, lo que claramente no tiene sentido. Gracias por la ayuda, y la validación en que 'hiarchies de la clase paralela' son un dolor para trabajar :) – Cam

2

En su exemple, puede convertir n en un DerivedNode si está seguro de que n es un ejemplo de DerivedNode, o puede utilizar los genéricos:

public class Base<N extends BaseNode> { 
    protected N n; 
    public void foo(BaseNode x){ 
     n.foo(x); 
    } 
} 


public class BaseNode { 
    public void foo(BaseNode x){...} 
} 

public class Derived extends Base<DerivedNode> { 
    public void bar(DerivedNode x){ 
     n.bar(x); // no problem here - n DOES have bar 
    } 
} 

public class DerivedNode extends BaseNode { 
    public void bar(BaseNode){ 
     ... 
    } 
} 
+0

¡Gracias! "En su ejemplo, puede convertir n en un DerivedNode si está seguro de que n es una instancia de DerivedNode" ¡fue extremadamente útil! – Cam

2

Las clases base no debe saber nada acerca de las clases derivadas de ellos, de lo contrario los problemas señalados anteriormente se levantará. Downcasting es un 'olor a código', y downcasting en la clase base para una clase derivada es particularmente 'apestoso'. Tales diseños pueden llevar a dependencias circulares difíciles de resolver también.

Si desea que una clase base haga uso de implementaciones de clase derivadas, utilice el patrón de método de plantilla, es decir, agregue un método virtual o abstracto en su clase base y anule e impleméntelo en la clase derivada. A continuación, puede llamar esto de forma segura desde la clase base.

Cuestiones relacionadas