2010-04-03 17 views
15

Warum kann ich eine Basisklasseninstanz nicht in eine abgeleitete Klasse umwandeln?Java; Casting-Basisklasse in abgeleitete Klasse

Zum Beispiel, wenn ich eine Klasse B habe, die eine Klasse C erweitert, warum kann ich das nicht tun?

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

oder das?

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

Okay lassen Sie mich genauer sein, was ich versuche zu tun. Hier ist, was ich habe:

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


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

Jetzt habe ich einen neuen Satz von Klassen erstellen möchten, die Basis und Basenode verlängern, wie folgt aus:

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

So im Wesentlichen ich neue Funktionalität zur Basis und BaseNode hinzufügen möchten indem Sie beide erweitern und beiden eine Funktion hinzufügen. Darüber hinaus sollten Base und BaseNode für sich alleine verwendet werden können.

Ich würde wirklich gerne dies ohne Generika tun, wenn möglich.


In Ordnung, also habe ich es herausgefunden, zum Teil dank Maruice Perry's Antwort.

In meinem Konstruktor für Base wird n instanziiert als BaseNode. Alles, was ich zu tun hatte, war wieder instanziieren n als DerivedNode in meiner abgeleiteten Klasse im Konstruktor, und es funktioniert perfekt.

+0

Zunächst müssen Sie BaseNode n machen; geschützt, damit Abgeleitetes es sehen kann. – Joel

+0

Woops (geändert). – Cam

+0

Ich meine, gibt es keinen Weg, dies ohne Generika zu tun? Wie ich schon sagte, ich möchte, dass die Basisklassen unverändert nutzbar sind - das Hinzufügen von Generics würde sie umständlicher machen und die Kapselung der BaseNode-Klasse entfernen, die von Base eingekapselt werden soll. – Cam

Antwort

6

Sie benötigen die Instanceof verwenden Stichwort die Art des Objekts durch n und Typumwandlung des Objekts referenziert zu überprüfen und rufen die bar() Methode. Kasse Derived.bar() Methode unten

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

denn wenn B C erstreckt, bedeutet dies, B eine C und nicht C ist ein B.

überdenken, was Sie zu tun versuchen.

+0

Ich habe meinen Beitrag bearbeitet und hinzugefügt, was ich eigentlich versuche zu tun. Irgendwelche Ideen? – Cam

2

Sie können das nicht tun, weil C implementieren nicht unbedingt die Verhaltensweisen, die Sie erstellt, wenn Sie es in B.

So

erweitert, sagen C foo() ein Verfahren hat. Dann weißt du, dass du foo() auf einem B anrufst, da B C ausdehnt, so dass du entsprechend ein Leckerbissen a B wie ein C mit (C)(new B()) wirken kannst.

Jedoch - wenn B eine Methode bar() hat, sagt nichts in der Unterklassenbeziehung, dass Sie bar() auf C auch anrufen können. Daher kann man ein C nicht so behandeln, als wäre es ein B, und deshalb kann man nicht casten.

0

Denn wenn B extends C, dann B vielleicht Sachen, die nicht in C (wie Instanzvariablen initialisieren Sie im Konstruktor, die nicht in neue C())

3

Sie einen Konstruktor für B erstellen können, dauert C als Parameter. Siehe this post für Ideen zu tun, was Sie versuchen zu tun.

16

Die vorhandenen Antworten sind gut in Bezug auf eine abstrakte Argumentation, aber ich möchte eine konkretere machen. Angenommen, Sie könnten tun.Dann würde dieser Code muss kompilieren und ausführen:

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

Wo genau würde die Umsetzung des read Verfahren kommen? Es ist abstrakt in InputStream. Woher würde es die Daten bekommen? Es ist einfach ist nicht geeignet, um eine bare java.lang.Object als InputStream zu behandeln. Es ist viel besser für die Besetzung, eine Ausnahme zu werfen.

Nach meiner Erfahrung ist es schwierig, "parallele Klassenhierarchien" wie die, die Sie beschreiben, zu arbeiten. Sie können finden, dass Generika helfen, aber es kann haarig sehr schnell bekommen.

+0

Danke Jon. Nach einigem Nachdenken macht das durchaus Sinn. Eine andere Erklärung könnte dies sein: Wir können bereits eine abgeleitete Klasse in eine Basisklasse umwandeln - was bedeutet, dass wir alles in ein Objekt umwandeln können. Wenn wir jedoch eine Basisklasse in eine abgeleitete Klasse umwandeln könnten, könnten wir ein Objekt effektiv als irgendetwas darstellen. Wenn wir diese beiden Dinge tun könnten, könnte jede Klasse in eine andere Klasse gegossen werden - was offensichtlich keinen Sinn ergibt. Vielen Dank für die Hilfe, und die Validierung in diesem 'parallelen Klassenhirchies' sind ein Schmerz zu arbeiten :) – Cam

2

In Ihrem exemple, können Sie n in eine DerivedNode werfen, wenn Sie sicher sind, dass n eine Instanz von DerivedNode ist, oder Sie verwenden können Generika:

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

Danke! "In Ihrem Beispiel können Sie n in einen DerivedNode umwandeln, wenn Sie sicher sind, dass n eine Instanz von DerivedNode ist" war sehr hilfreich! – Cam

2

Basisklassen sollten nichts über Klassen wissen von ihnen abgeleiteten, sonst markiert die oben genannten Probleme auftreten werden. Downcasting ist ein "Code-Geruch", und Downcasting in der Basisklasse in eine abgeleitete Klasse ist besonders "stinkend". Solche Designs können auch zu schwer auflösbaren zirkulären Abhängigkeiten führen.

Wenn Sie möchten, dass eine Basisklasse abgeleitete Klassenimplementierungen verwendet, verwenden Sie das Vorlagenmethodenmuster, d. H. Fügen Sie eine virtuelle oder abstrakte Methode in Ihre Basisklasse ein und überschreiben und implementieren Sie sie in der abgeleiteten Klasse. Sie können dies dann sicher von der Basisklasse aus aufrufen.

Verwandte Themen