[英]Java; casting base class to derived class
為什么我不能將基類實例轉換為派生類?
例如,如果我有一個擴展C類的B類,為什么我不能這樣做呢?
B b=(B)(new C());
或這個?
C c=new C();
B b=(B)c;
好吧,讓我更具體地說明我正在做什么。 這就是我所擁有的:
public class Base(){
protected BaseNode n;
public void foo(BaseNode x){
n.foo(x);
}
}
public class BaseNode(){
public void foo(BaseNode x){...}
}
現在我想創建一組擴展Base和Basenode的新類,如下所示:
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){
...
}
}
所以基本上我想通過擴展它們並向它們添加一個函數來向Base和BaseNode添加新功能。 此外,Base和BaseNode應該可以單獨使用。
如果可能的話,我真的很想在沒有泛型的情況下這樣做。
好吧,所以我最終搞清楚了,部分歸功於Maruice Perry的回答。
在我的Base
構造函數中, n
被實例化為BaseNode
。 我所要做的就是在構造函數的派生類中將n
重新實例化為DerivedNode
,它完美地運行。
因為如果B擴展C,則意味着B是C而不是C是B.
重新思考你想要做什么。
現有答案在抽象論證方面很好,但我想做一個更具體的答案。 假設你可以那樣做。 然后這段代碼必須編譯並運行:
// Hypothetical code
Object object = new Object();
InputStream stream = (InputStream) object; // No exception allowed?
int firstByte = stream.read();
read
方法的實現究竟在哪里? 它在InputStream
是抽象的。 從哪里獲取數據? 將一個簡單的java.lang.Object
視為InputStream
是不合適的。 對於演員而言拋出異常要好得多。
根據我的經驗,獲得“並行類層次結構”是很棘手的,就像你描述的那樣。 您可能會發現仿制葯有所幫助,但它可以很快變得毛茸茸。
您需要使用instanceof關鍵字來檢查由n引用的對象的類型,並對該對象進行類型轉換並調用bar()方法。 下面是Checkout Derived.bar()方法
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" );
}
}
您可以為B創建一個構造函數,它將C作為參數。 請參閱此帖子 ,了解您正在嘗試做的事情。
基類不應該知道從它們派生的類的任何內容,否則將出現上面突出顯示的問題。 向下轉換是一種“代碼味道”,並且在基類中向衍生類的向下轉換特別“臭”。 這樣的設計也可能導致難以解決循環依賴性。
如果希望基類使用派生類實現,請使用Template方法模式,即在基類中添加虛擬或抽象方法,並在派生類中重寫並實現它。 然后,您可以從基類安全地調用它。
你不能這樣做,因為C不一定實現你在B中擴展它時創建的行為。
所以,假設C有一個方法foo()
。 然后你知道你可以在B上調用foo()
,因為B擴展了C,所以你可以相應地對B進行轉換,就好像它是一個帶有(C)(new B())
。
但是 - 如果B有一個方法bar()
,子類關系中的任何內容都表示你也可以在C上調用bar()
。 因此,您不能將C視為B,因此您無法投射。
在您的示例中,如果您確定n是DerivedNode的實例,則可以將n強制轉換為DerivedNode,或者您可以使用泛型:
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){
...
}
}
因為如果B extends C
,那么B可能有不在C中的東西(比如在構造函數中初始化的實例變量不在新的C()中)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.