Java - 继承的流畅方法返回类型应返回实例类的类型,而不是父类的类型

5
当您采用流畅的方法时,您可能会遇到以下情况:
public class Foo<T extends a>{

    public Foo<T> someMethod(){
          System.out.println("foo");
          return this;
    }

}


public class Bar<T extends b> extends Foo<T> {

         public Bar<T> barMethod(){

            System.out.println("bar");
            return this;
         }

}


public class a{}
public class b extends a{}

流畅接口的优点在于您可以链接方法调用,但是尽管Bar继承了someMethod(),返回类型是Foo而不是Bar,这将打破以下链接:
new Bar<b>().someMethod().barMethod();

一个答案是为每个继承的方法添加@Overrides,这样Bar就有了额外的方法:
 @Override
 public Bar<T> someMethod(){
          System.out.println("foo");
          return this;
    }

在大型类和扩展的类层次结构中,这可能会造成冗余混乱。有没有适当的返回类型可以给流畅方法,使得继承它的每个类都会返回其特定类型的对象(以便我们可以在不使用转换的情况下链接方法)?
我尝试过:
        public <U extends Foo<T>> U someMethod(){
          System.out.println("foo");
          return this;
    }

很遗憾,目前还没有有效的解决方案。我希望有人能提供一个简单而优雅的解决方案。由于项目较大,因此需要尽可能地易于维护和扩展。

非常感谢您提供的任何帮助。

2个回答

5
如果您不介意未经检查的转换,则可以按如下方式执行此操作:
public class Foo<T, F extends Foo<T, F>> {
  public F someMethod() {
    System.out.println("foo");
    return (F) this; // <--- That's the unchecked cast! Tucked safely away.
  }
  public static void main(String[] args) {
    new Bar<String>().someMethod().barMethod();
  }
}

class Bar<T> extends Foo<T, Bar<T>> {
  public Bar<T> barMethod() {
    System.out.println("bar");
    return this;
  }
}

个人而言,我会全局禁用整个项目中的未经检查的强制类型转换警告。


但是仍然存在一个“转换”,这不是OP想要的。我不知道为什么。嗯。但是这个转换更好。它在一个地方。所以,OP应该对此感到满意。否则,上帝保佑他。;) - Rohit Jain
1
@RohitJain,请看我在上面答案中的评论:这是一个可用性问题,试图使客户在其项目中包含一个非常简单直接的库。Marko Topolnik的解决方案很有趣,这是我没有考虑过的。似乎某些类型的转换是不可避免的? - Darius
@Marko Topolnik,您说“全局禁用未经检查的转换警告”,那么是否会传播到客户端代码,以便在使用库时不会收到任何警告? - Darius
如果客户端只得到一个JAR文件,那么一切都已经结束了,因为这是方法实现中仅在编译时进行的检查。从外部甚至无法检测到它。 - Marko Topolnik
1
我在这种方法中遇到了更多的困难...我仍然怀疑它是否适用于更深层次的层次结构。 - Marko Topolnik
显示剩余11条评论

1
你可以定义barMethod方法为抽象方法,这样你就可以在Foo类型上调用它。
public abstract class Foo<T extends a> {

    public Foo<T> someMethod() {
        System.out.println("foo");
        return this;
    }

    public abstract Foo<T> barMethod();
}

现在您可以轻松地进行呼叫

new Bar<b>().someMethod().barMethod();

这种方法的问题在于它阻止了我们直接实例化 Foo,而我仍然需要有能力这样做。 - Darius
@HodeCode。所以你不想添加抽象方法,也不想覆盖,也不想进行类型转换。嗯。如果有办法的话会很困难。让我们看看是否有人能提出解决方案。但是如果你削减了所有的变通方法,我认为就没有绕过的方法了。但我也不能确定。 - Rohit Jain
然后你将不得不进行类型转换。 - Amit Deshpande
1
真遗憾的是,Java似乎并不优雅地支持这种架构。我正在尝试使客户端使用库变得尽可能简单,而不需要任何充斥着强制转换等丑陋代码的情况。 - Darius
@HodeCode 它是抽象类的好候选,因为您已经有了它的子类,并且在Foo类中具有共同功能。 - Amit Deshpande

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接