【问题标题】:Way to make Java parent class method return object of child class使Java父类方法返回子类对象的方法
【发布时间】:2010-10-27 09:49:30
【问题描述】:

当从子类对象调用此方法时,是否有任何优雅的方法可以使位于父类中的Java方法返回子类的对象?

我想在不使用额外接口和额外方法的情况下实现它,并且在不使用类强制转换、辅助参数等的情况下使用它。

更新:

抱歉,我没说清楚。

我想实现方法链接,但是我对父类的方法有问题:当我调用父类方法时,我无法访问子类方法... 我想我已经介绍了我的想法。

所以方法应该返回this.getClass()类的this对象。

【问题讨论】:

    标签: java generics


    【解决方案1】:

    如果您只是在寻找针对已定义子类的方法链接,那么以下应该可以工作:

    public class Parent<T> {
    
      public T example() {
        System.out.println(this.getClass().getCanonicalName());
        return (T)this;
      }
    }
    

    如果你愿意,可以是抽象的,然后是一些指定通用返回类型的子对象(这意味着你不能从 ChildA 访问 childBMethod):

    public class ChildA extends Parent<ChildA> {
    
      public ChildA childAMethod() {
        System.out.println(this.getClass().getCanonicalName());
        return this;
      }
    }
    
    public class ChildB extends Parent<ChildB> {
    
      public ChildB childBMethod() {
        return this;
      }
    }
    

    然后你就这样使用它

    public class Main {
    
      public static void main(String[] args) {
        ChildA childA = new ChildA();
        ChildB childB = new ChildB();
    
        childA.example().childAMethod().example();
        childB.example().childBMethod().example();
      }
    }
    

    输出将是

    org.example.inheritance.ChildA 
    org.example.inheritance.ChildA 
    org.example.inheritance.ChildA 
    org.example.inheritance.ChildB 
    org.example.inheritance.ChildB
    

    【讨论】:

    • 非常感谢。这正是我想要的。
    • 完美。但我需要将@SuppressWarnings("unchecked") 添加到example()
    • 这适用于 OP,但不幸的是对我(以及this question 中的 OP)来说,如果您还想制作 GrandChild extend ChildA,它就不起作用。 =(
    • 应该是 Parent>,不是吗?
    • 我正在使用这个实现,但 IDE 会警告 Unchecked cast: 'mypackage.MyClass&lt;T&gt; to T'。也许这些年来还有另一种方式?
    【解决方案2】:

    你想达到什么目的?这听起来是个坏主意。父类不应该知道任何关于它的孩子的事情。它似乎非常接近打破Liskov Substitution Principle。我的感觉是,通过更改总体设计会更好地服务于您的用例,但如果没有更多信息就很难说。

    对不起,听起来有点迂腐,但当我看到这样的问题时,我有点害怕。

    【讨论】:

    • 我的用例如下:我有描述 UI 屏幕功能的类。然后我使用以下语法在屏幕上执行多个操作:currentScreen.actionA().actionB().actionC(); 但是,如果一个操作对所有屏幕都是通用的,因此我想在父类中实现它怎么办?在这种情况下,我希望父类中的方法与子对象一起返回,这样我就可以继续在子屏幕上调用操作,就好像所有操作都作为子类的方法实现一样。
    • 它不违反里氏替换原则。这只是 Java 语法的一个怪癖,以避免显式强制转换和编译器警告。
    【解决方案3】:

    只是为了演示:

    public Animal myMethod(){
      if(this isinstanceof Animal){
         return new Animal();
      }
      else{
    
         return this.getClass().newInstance();
      }
    }
    

    【讨论】:

    • 随着子类的添加、更改和删除,必须一次又一次地更改此方法。尽管如此,仍然是一个解决方案。
    • @BoltClock,只是演示,我已经在这次更新中解决了你的问题。
    • 如何定义 new Animal(TigerData) 返回 Tiger 实例和 new Animal(LionData) 返回 Lion 实例。谢谢
    【解决方案4】:

    您可以调用this.getClass()获取运行时类。

    但是,这不一定是调用该方法的类(它可能在层次结构的更下方)。

    而且你需要使用反射来创建新的实例,这很棘手,因为你不知道子类有什么样的构造函数。

    return this.getClass().newInstance(); // sometimes works
    

    【讨论】:

    • 这听起来像:父母可以通过反思产生一个孩子,但不能保证是他的。 :)
    • 它将成为当前类的子类,因为this 总是如此。不过,它可能是一个间接子类。
    【解决方案5】:

    我完全知道你的意思,在 Perl 中有 $class 变量,这意味着如果你在子类上调用某个工厂方法,即使它没有在子类中被覆盖,如果它实例化了 $class 的任何实例将创建一个子类的实例。

    Smalltalk、Objective-C、许多其他语言都有类似的功能。

    唉,Java 中没有这样的等效工具。

    【讨论】:

    • 在 Perl 中根本没有通用的内置 $class 变量。 可以通过检索类方法的第一个参数来计算(例如,MyClass-&gt;mySub() 调用将"MyClass" 作为第一个参数传递)或通过获取对象的ref()(这是第一个在对象上调用的方法的参数,例如$myclass_object-&gt;mySub() 调用将$myclass_object 作为第一个参数传递,ref($myclass_object) 返回"MyClass")。请知道你在说什么。
    • 但是,正如您所说,类方法的第一个参数始终是应用该方法的类的名称。这意味着您可以编写方法然后执行$class-&gt;newInstance(),如果在子类上调用该方法,它将创建子类的实例。对工厂方法、构造中的模板方法模式等很有用。在 Java 中,static MyObj newInstance() { .. } 将始终生成 MyObj,即使您调用 MySubObj.newInstance() 也是如此,并且没有办法像例如在 Perl 中。
    【解决方案6】:

    如果你使用 Kotlin,你可以创建一个扩展函数

    abstract class SuperClass
    class SubClass: SuperClass()
    
    fun <T : SuperClass> T.doSomething(): T {
        // do something
        return this
    }
    
    val subClass = SubClass().doSomething()
    

    【讨论】:

      【解决方案7】:
      public class Parent {
          public Parent myMethod(){
              return this;
          }
      }
      public class Child extends Parent {}
      

      然后像这样调用它

             Parent c = (new Child()).myMethod();
             System.out.println(c.getClass());
      

      这个解决方案是否正确?如果是,那么它与 #1 解决方案有何不同?

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-06-12
        • 2011-04-05
        • 1970-01-01
        • 2011-04-17
        • 1970-01-01
        相关资源
        最近更新 更多