2010-04-03 5 views
15

Pourquoi ne puis-je pas convertir une instance de classe de base en classe dérivée? Par exemple, si j'ai une classe B qui étend une classe C, pourquoi ne puis-je pas le faire?Java; conversion de la classe de base en classe dérivée

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

ou this?

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

Bon laissez-moi être plus précis quant à ce que je suis en train de faire. Voici ce que j'ai:

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


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

Maintenant, je veux créer un nouvel ensemble de classes qui s'étendent la base et Basenode, comme ceci:

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){ 
     ... 
    } 
} 

donc essentiellement que je veux ajouter de nouvelles fonctionnalités à la base et BaseNode en les prolongeant tous les deux, et en ajoutant une fonction à chacun d'eux. De plus, Base et BaseNode devraient pouvoir être utilisés seuls.

Je voudrais vraiment faire cela sans génériques si possible. D'accord, j'ai fini par trouver une solution, en partie grâce à la réponse de Maruice Perry. Dans mon constructeur pour Base, n est instancié en tant que BaseNode. Tout ce que j'avais à faire était de ré-instancier n en tant que DerivedNode dans ma classe dérivée dans le constructeur, et cela fonctionne parfaitement.

+0

Tout d'abord, vous devez créer BaseNode n; protégé afin que Derived puisse le voir. – Joel

+0

Woops (l'a changé). – Cam

+0

Je veux dire qu'il n'y a aucun moyen de le faire sans génériques? Comme je l'ai dit, je veux que les classes de base soient utilisables telles quelles, l'ajout de génériques les rendrait plus encombrantes à utiliser et supprime l'encapsulation de la classe BaseNode qui est censée être encapsulée par Base. – Cam

Répondre

6

Vous devez utiliser le instanceof mot-clé pour vérifier le type d'objet référencé par n et typecast l'objet et appeler le bar() méthode. Commander Derived.bar() méthode ci-dessous

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

parce que si B C se prolonge, cela signifie que B est un C et non C est un B.

ce que vous repensons essayez de faire.

+0

J'ai modifié mon post et ajouté ce que j'essaie réellement de faire. Des idées? – Cam

2

Vous ne pouvez pas le faire parce que C ne met pas nécessairement les comportements créés lorsque vous l'avez prolongé dans B.

Alors, dites-C a une méthode foo(). Alors vous savez que vous pouvez appeler foo() sur un B, comme B étend C, de sorte que vous pouvez lancer en conséquence un régaler un B comme s'il s'agissait d'un C avec (C)(new B()).

Cependant - si B a une méthode bar(), rien dans la relation de sous-classe ne dit que vous pouvez également appeler bar() sur C. Ainsi, vous ne pouvez pas traiter un C comme s'il s'agissait d'un B, et vous ne pouvez donc pas lancer.

0

Parce que si B extends C, alors B pourrait avoir des choses qui ne sont pas en C (comme variables d'instance vous initialiser dans le constructeur qui ne sont pas dans la nouvelle C())

3

Vous pouvez créer un constructeur B qui prend C en tant que paramètre Voir this post pour des idées pour faire ce que vous essayez de faire.

16

Les réponses existantes sont bonnes en termes d'argument abstrait, mais j'aimerais en faire une plus concrète. Supposons que puisse faire.Ensuite, ce code devrait compiler et exécuter:

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

Où serait exactement la mise en œuvre de la méthode read venir? C'est abstrait en InputStream. D'où obtiendraient-ils les données? Il simplement n'est pas approprié pour traiter un java.lang.Object nu comme InputStream. Il est préférable que la distribution lance une exception. Dans mon expérience, il est difficile d'obtenir des «hiérarchies de classes parallèles» comme celle que vous décrivez pour fonctionner. Vous pouvez trouver que les génériques aident, mais il peut devenir poilu très rapidement.

+0

Merci Jon. Après quelques réflexions supplémentaires, cela a du sens. Une autre explication pourrait être la suivante: nous pouvons déjà lancer une classe dérivée vers une classe de base - ce qui signifie que nous pouvons tout donner à un objet. Cependant, si nous pouvions convertir une classe de base en une classe dérivée, nous pourrions effectivement lancer un Object comme n'importe quoi. Si nous pouvions faire ces deux choses, n'importe quelle classe pourrait être castée dans n'importe quelle autre classe - ce qui n'a aucun sens. Merci pour l'aide, et la validation en ce que «Hiarchies classe parallèle» sont une douleur à travailler :) – Cam

2

Dans votre exemple, vous pouvez lancer n dans un DerivedNode si vous êtes certain que n est une instance de DerivedNode, ou vous pouvez utiliser les génériques:

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

Merci! "Dans votre exemple, vous pouvez lancer n dans un DerivedNode si vous êtes certain que n est une instance de DerivedNode" a été extrêmement utile! – Cam

2

Les classes de base ne doivent pas savoir quoi que ce soit sur les classes qui en dérivent, sinon les problèmes mis en évidence ci-dessus se posera. La downcasting est une «odeur de code», et le downcasting dans la classe de base vers une classe dérivée est particulièrement «malodorant». De telles conceptions peuvent également rendre difficile la résolution de dépendances circulaires. Si vous voulez qu'une classe de base utilise des implémentations de classes dérivées, utilisez le modèle de méthode Template i.e Ajoutez une méthode virtuelle ou abstraite dans votre classe de base et remplacez-la et implémentez-la dans la classe dérivée. Vous pouvez ensuite l'appeler en toute sécurité à partir de la classe de base.

Questions connexes