有没有办法用typesvariables引用当前types?

假设我试图编写一个函数来返回当前types的一个实例。 有没有办法让T指的是确切的子types(所以T应该指B中的B )?

 class A { <T extends A> foo(); } class B extends A { @Override T foo(); } 

为了build立在StriplingWarrior的答案 ,我认为下面的模式将是必要的(这是一个层次stream利的build设者API的食谱)。

首先,一个基本的抽象类(或接口),为扩展该类的实例的运行时types设置合同:

 /** * @param <SELF> The runtime type of the implementor. */ abstract class SelfTyped<SELF extends SelfTyped<SELF>> { /** * @return This instance. */ abstract SELF self(); } 

所有的中间扩展类都必须是abstract并保持recursiontypes参数SELF

 public abstract class MyBaseClass<SELF extends MyBaseClass<SELF>> extends SelfTyped<SELF> { MyBaseClass() { } public SELF baseMethod() { //logic return self(); } } 

其他派生类可以按照相同的方式。 但是,这些类都不能直接作为variables的types使用,而不使用原型或通配符(这会破坏模式的目的)。 例如(如果MyClass不是abstract ):

 //wrong: raw type warning MyBaseClass mbc = new MyBaseClass().baseMethod(); //wrong: type argument is not within the bounds of SELF MyBaseClass<MyBaseClass> mbc2 = new MyBaseClass<MyBaseClass>().baseMethod(); //wrong: no way to correctly declare the type, as its parameter is recursive! MyBaseClass<MyBaseClass<MyBaseClass>> mbc3 = new MyBaseClass<MyBaseClass<MyBaseClass>>().baseMethod(); 

这就是我把这些课程称为“中级”的原因,这就是为什么他们都应该被标记为abstract 。 为了closures循环并使用该模式,需要“叶”类,它用自己的typesparsinginheritance的types参数SELF并实现self() 。 他们也应该被标记为final以避免违约:

 public final class MyLeafClass extends MyBaseClass<MyLeafClass> { @Override MyLeafClass self() { return this; } public MyLeafClass leafMethod() { //logic return self(); //could also just return this } } 

这样的类使模式可用:

 MyLeafClass mlc = new MyLeafClass().baseMethod().leafMethod(); AnotherLeafClass alc = new AnotherLeafClass().baseMethod().anotherLeafMethod(); 

这里的值是方法调用可以在类层次结构中链接,同时保持相同的特定返回types。


免责声明

以上是Java中奇怪的循环模板模式的实现。 这种模式本质并不安全 ,只能用于内部API的内部工作。 原因在于,不能保证上述示例中的types参数SELF实际上将被parsing为正确的运行时types。 例如:

 public final class EvilLeafClass extends MyBaseClass<AnotherLeafClass> { @Override AnotherLeafClass self() { return getSomeOtherInstanceFromWhoKnowsWhere(); } } 

这个例子公开了这个模式中的两个漏洞:

  1. EvilLeafClass可以“说谎”,并替代扩展MyBaseClass任何其他types的SELF
  2. 除此之外,不能保证self()实际上会返回this ,这可能是也可能不是问题,这取决于基本逻辑中状态的使用。

由于这些原因,这种模式很有可能被滥用或滥用。 为了防止这种情况发生,请不要将涉及的类公开扩展 – 注意我在MyBaseClass使用了包 – 私有构造MyBaseClass ,该构造函数replace了隐式的公共构造函数:

 MyBaseClass() { } 

如果可能的话,还要保持self()包的私有性,所以它不会给公共API添加噪音和混淆。 不幸的是,只有当SelfTyped是一个抽象类时才可能,因为接口方法是隐式公开的。

正如zhong.j.yu在评论中指出的那样 , SELF的界限可能会被简单地删除,因为它最终无法确保“自我types”:

 abstract class SelfTyped<SELF> { abstract SELF self(); } 

余build议只依靠合同,避免来自不直观的recursion界限的混淆或虚假的安全感。 就我个人而言,我更喜欢离开界限,因为SELF extends SelfTyped<SELF>表示Java中自我types的最接近的expression式。 但是俞的观点与“ Comparable的先例Comparable


结论

这是一个有价值的模式,可以对您的构build器API进行stream畅而富有performance力的调用。 我已经使用过很多次了,特别是编写一个定制的查询生成器框架,它允许这样的调用网站:

 List<Foo> foos = QueryBuilder.make(context, Foo.class) .where() .equals(DBPaths.from_Foo().to_FooParent().endAt_FooParentId(), parentId) .or() .lessThanOrEqual(DBPaths.from_Foo().endAt_StartDate(), now) .isNull(DBPaths.from_Foo().endAt_PublishedDate()) .or() .greaterThan(DBPaths.from_Foo().endAt_EndDate(), now) .endOr() .or() .isNull(DBPaths.from_Foo().endAt_EndDate()) .endOr() .endOr() .or() .lessThanOrEqual(DBPaths.from_Foo().endAt_EndDate(), now) .isNull(DBPaths.from_Foo().endAt_ExpiredDate()) .endOr() .endWhere() .havingEvery() .equals(DBPaths.from_Foo().to_FooChild().endAt_FooChildId(), childId) .endHaving() .orderBy(DBPaths.from_Foo().endAt_ExpiredDate(), true) .limit(50) .offset(5) .getResults(); 

关键在于QueryBuilder不仅仅是一个扁平的实现,而是从一个复杂的构build器类层次中延伸出来的“叶子”。 像WhereHavingOr等帮助者使用相同的模式,所有这些都需要共享重要的代码。

然而,你不应该忽略这一切,最终只能是语法糖。 一些经验丰富的程序员对CRT模式采取强硬立场 ,或者至less对其增加的复杂性所带来的好处持怀疑态度 。 他们的担忧是合法的。

底线,在实施之前要认真考虑是否真的有必要 – 如果你这样做,不要公开扩展。

您应该能够使用Java用于枚举的recursiongenerics定义样式来执行此操作:

 class A<T extends A<T>> { T foo(); } class B extends A<B> { @Override B foo(); } 

只要写:

 class A { A foo() { ... } } class B extends A { @Override B foo() { ... } } 

假设你正在使用Java 1.5+( 协变返回types )。

如果你想要类似于Scala的东西

 trait T { def foo() : this.type } 

那么不,在Java中这是不可能的。 你也应该注意到,除了this之外,你可以从Scala中类似的函数中返回的东西不多。

我发现了一个办法 ,这是一种愚蠢的,但它的作品:

在顶级课程(A)中:

 protected final <T> T a(T type) { return type } 

假设C扩展B和B扩展A.

调用:

 C c = new C(); //Any order is fine and you have compile time safety and IDE assistance. c.setA("a").a(c).setB("b").a(c).setC("c"); 

我可能不完全明白这个问题,但仅仅这样做还不够(注意铸造到T):

  private static class BodyBuilder<T extends BodyBuilder> { private final int height; private final String skinColor; //default fields private float bodyFat = 15; private int weight = 60; public BodyBuilder(int height, String color) { this.height = height; this.skinColor = color; } public T setBodyFat(float bodyFat) { this.bodyFat = bodyFat; return (T) this; } public T setWeight(int weight) { this.weight = weight; return (T) this; } public Body build() { Body body = new Body(); body.height = height; body.skinColor = skinColor; body.bodyFat = bodyFat; body.weight = weight; return body; } } 

那么子类将不必使用重写或协方差types来使母类方法返回对它们的引用…

  public class PersonBodyBuilder extends BodyBuilder<PersonBodyBuilder> { public PersonBodyBuilder(int height, String color) { super(height, color); } }