web-dev-qa-db-fra.com

Java; conversion de la classe de base en classe dérivée

Pourquoi ne puis-je pas convertir une instance de classe de base en une classe dérivée?

Par exemple, si j'ai une classe B qui étend une classe C, pourquoi ne puis-je pas le faire?

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

ou ca?

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

D'accord, permettez-moi d'être plus précis sur ce que j'essaie 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 étendent 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, je veux ajouter de nouvelles fonctionnalités à Base et BaseNode en les étendant toutes les deux et en ajoutant une fonction aux deux. De plus, Base et BaseNode devraient pouvoir être utilisés seuls.

J'aimerais vraiment le faire sans génériques si possible.


Très bien donc j'ai fini par comprendre, en partie grâce à la réponse de Maruice Perry.

Dans mon constructeur pour Base, n est instancié comme 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.

19
Cam

Vous devez utiliser le mot clé instanceof pour vérifier le type d'objet référencé par n et transtypez l'objet et appelez la barre () = méthode. Paiement 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" );
    }
}
6
Babar

parce que si B étend C, cela signifie que B est un C et non C est un B.

repensez ce que vous essayez de faire.

20
Omry Yadan

Les réponses existantes sont bonnes en termes d'argument abstrait, mais j'aimerais en faire un plus concret. Supposons que vous pourriez faire cela. 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();

D'où proviendrait exactement l'implémentation de la méthode read? C'est abstrait dans InputStream. D'où obtiendrait-il les données? Il suffit n'est pas approprié de traiter un Java.lang.Object Nu comme un InputStream. Il est préférable pour le casting de lever une exception.

D'après mon expérience, il est difficile d'obtenir des "hiérarchies de classes parallèles" comme celle que vous décrivez pour fonctionner. Vous mai trouvez que les génériques aident, mais ils peuvent devenir poilus très rapidement.

19
Jon Skeet

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

4
Joel

Vous ne pouvez pas faire cela car C n'implémente pas nécessairement les comportements que vous avez créés lorsque vous l'avez étendu en B.

Donc, disons que C a une méthode foo(). Ensuite, vous savez que vous pouvez appeler foo() sur un B, car B étend C, vous pouvez donc convertir en conséquence un traitement B comme si c'était 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 aussi 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.

2
brabster

Dans votre exemple, vous pouvez convertir n en un DerivedNode si vous êtes certain que n est une instance de DerivedNode, ou vous pouvez utiliser des 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){
        ...
    }
}
2
Maurice Perry

Les classes de base ne devraient rien savoir des classes qui en dérivent, sinon les problèmes mis en évidence ci-dessus se poseront. Le downcasting est une "odeur de code", et le downcasting dans la classe de base vers une classe dérivée est particulièrement "puant". De telles conceptions peuvent également conduire à des dépendances circulaires difficiles à résoudre.

Si vous souhaitez qu'une classe de base utilise des implémentations de classe dérivée, utilisez le modèle de méthode Template, c'est-à-dire 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.

2
rich s

Parce que si B extends C, alors B pourrait avoir des éléments qui ne sont pas en C (comme les variables d'instance que vous initialisez dans le constructeur qui ne sont pas dans le nouveau C ())

0
glebm