当 MySubClass 扩展 MyClass 时,在 MyClass 中使用 return MyClass 对象的方法

Using method that return MyClass object within MyClass when MySubClass extends MyClass

我在 subclass 一个非常简单的 class 上遇到问题,它也有返回初始 class 的方法。

public class MyClass {

   public MyClass(){
     }

   public MyClass filterOn(String something){
       MyClass result=new MyClass();
       result.doSomethingUsingThisInstance(this, something);

      return result;
   }

}


 public class MySubClass extends MyClass{
   ....
 }

好的,现在如果我想这样调用:

  MySubClass subClass=new MySubClass();
  MySubClass subClass2=(MySubClass)subClass.filterOn("Hello World");

然后我有一个java.lang.ClassCastException:cannot cast MyClass to MySubClass

如何防止这种情况?

(MySubClass)subClass.filterOn("Hello World");

public MyClass filterOn(String something)

您不能将基 class 转换为派生 class。如果你这样做,你会得到那个例外,ClassCastException

读这个:java.lang.ClassCastException

覆盖 filterOn() 方法以在 MySubClass 中创建您想要的实例:

 public class MySubClass extends MyClass{

    public MyClass filterOn(String something){
       MySubClass result = new MySubClass();
       result.doSomethingUsingThisInstance(this, something);
       return result;
    }
   ....
 }

您还可以通过在 MyClass 中引入一个方法来创建我们在 subclass 中覆盖的当前 class 的实例,从而避免 filterOn() 方法中的重复:

public class MyClass {

   public MyClass(){
     }

   public MyClass createSpecificInstance() {
     return new MyClass();
   }

   public MyClass filterOn(String something){
       MyClass result = createSpecificInstance();
       result.doSomethingUsingThisInstance(this, something);

      return result;
   }

}

现在 Sub class 仅覆盖 createSpecificInstance() :

public class MySubClass  extends MyClass {

   public MyClass createSpecificInstance() {
     return new MySubClass();
   }

}

您尝试将 return 值转换为派生类型,这不起作用并导致 class 转换异常。

原因:在类型层次结构中向上转换是有效的,因为你的基础 class 只需要派生的 class(通常)具有的属性/方法。反之则行不通,因为它可能会导致问题。考虑:

class Base {
// some stuff here
}

class Derived1 extends Base {
  private int foo;
}

class Derived2 extends Base {
  private String bar;
}

Base myObject = new Derived1(); 
// this works, Derived1 has everything Base requires
// myObject.foo still exists, but can not be trivially accessed.

Derived2 myOtherObject = (Derived2)myObject;
// Now what? 
// What happens to myObject.foo? 
// Where does myOtherObject.bar come from?

在你的情况下你能做什么:

  • 如果 filterOn() 的实现根据派生 class 的具体实现有很大不同,请在基础 class 中将其 abstract =] 并在派生的 class.
  • 中重新实现它
  • 检查您是否有设计问题。您真的需要将 filterOn() 的结果转换为派生的 class 吗?
  • 使用Generics。请记住,您可以使用派生 class 作为基础 class 中的泛型。仅当 filterOn() 的实现对于每个子 class 完全相同(当然类型除外)时才有效。
  • 提供一个允许从基础 class 创建派生 class 实例的构造函数。使用它而不是强制转换。符合
  • 的东西
Derived1(Base baseObject){
  // copy all the stuff from the base object
  this.foo = 0; // initialize the rest
}
  • 也许继承不是你所需要的。组合有很多击败继承的趋势(Explained nicely here)。所以你可以试试:
class BetterThenDerived {
  private Base myBaseObject;
  private int foo;
}

这是协方差问题的一个方面。参见示例 Covariance and contravariance. And Demonstrate covariance and contravariance in Java?。这并不是 Java 擅长的地方,但有几个选择。

首先,重写方法时,您可以声明更具体的 return 类型。例如,在 MySubClass 中你可以声明:

@Override
public MySubClass filterOn(String something) {
    // ...
}

现在这并不能解决你的问题。您仍然需要一种方法让此方法创建 MySubClass 对象并对其执行某些操作。它所做的是,它使客户端代码完全不需要强制转换。你可以采取davidxxx的答案中方法的实现(前提是result.doSomethingUsingThisInstance()protectedpublic):

@Override
public MySubClass filterOn(String something) {
    MySubClass result = new MySubClass();
    result.doSomethingUsingThisInstance(this, something);
    return result;
}

您可能对必须在所有子类中复制此方法感到恼火。如果真正的工作留在 result.doSomethingUsingThisInstance() 我想你可能会接受它。

另一个想法是 clone() 用于生成正确运行时类型的对象:

public class MyClass implements Cloneable {

    public MyClass filterOn(String something) {
        try {
            MyClass result = (MyClass) this.clone();
            result.doSomethingUsingThisInstance(this, something);

            return result;
        } catch (CloneNotSupportedException cnse) {
            throw new AssertionError(cnse);
        }
    }

}

不过,在使用这个想法之前我会三思而后行。您的克隆包含您不希望存在的数据的一个原因是,您可能必须确保在处理克隆之前已将其清除。您仍然可以将它与 returning 子类中的特定子类型结合起来,现在它只需要一个转换(仍然在实现中,客户端不需要担心):

public class MySubClass extends MyClass {

    @Override
    public MySubClass filterOn(String something) {
        return (MySubClass) super.filterOn(something);
    }

}