Java 是否有方法使用类型变量引用当前类型?
假设我试图编写一个函数来返回当前类型的实例。有没有办法让Java 是否有方法使用类型变量引用当前类型?,java,generics,types,Java,Generics,Types,假设我试图编写一个函数来返回当前类型的实例。有没有办法让T引用确切的子类型(因此T应该在B类中引用B) A类{ foo(); } B类扩展了A类{ @凌驾 T foo(); } 只要写下: class A { A foo() { ... } } class B extends A { @Override B foo() { ... } } 假设您使用的是Java 1.5+()。您应该能够使用Java用于枚举的递归通用定义样式来实现这一点: class A<T e
T
引用确切的子类型(因此T
应该在B
类中引用B
)
A类{
foo();
}
B类扩展了A类{
@凌驾
T foo();
}
只要写下:
class A {
A foo() { ... }
}
class B extends A {
@Override
B foo() { ... }
}
假设您使用的是Java 1.5+()。您应该能够使用Java用于枚举的递归通用定义样式来实现这一点:
class A<T extends A<T>> {
T foo();
}
class B extends A<B> {
@Override
B foo();
}
A类{
T foo();
}
B类扩展了A类{
@凌驾
B foo();
}
如果您想要类似Scala的东西
trait T {
def foo() : this.type
}
那么不,这在Java中是不可能的。您还应该注意到,在Scala中,类似类型的函数没有多少可以返回的,除了要构建的this
之外,我认为以下模式是必要的(这是分层fluent builder API的配方)
解决方案
首先,一个基本抽象类(或接口),它列出了用于返回扩展该类的实例的运行时类型的契约:
/**
* @param <SELF> The runtime type of the implementor.
*/
abstract class SelfTyped<SELF extends SelfTyped<SELF>> {
/**
* @return This instance.
*/
abstract SELF self();
}
其他派生类也可以采用相同的方式。但是,如果不使用rawtypes或通配符(这违背了模式的目的),这些类都不能直接用作变量类型。例如(如果MyClass
不是abstract
):
这些类使模式可用:
MyLeafClass mlc = new MyLeafClass().baseMethod().leafMethod();
AnotherLeafClass alc = new AnotherLeafClass().baseMethod().anotherLeafMethod();
这里的值是方法调用可以在类层次结构上下链接,同时保持相同的特定返回类型
免责声明 上面是Java中的一个实现。此模式不是天生安全的,应仅保留用于内部API的内部工作。原因是无法保证上述示例中的类型参数
SELF
将实际解析为正确的运行时类型。例如:
public final class EvilLeafClass extends MyBaseClass<AnotherLeafClass> {
@Override
AnotherLeafClass self() {
return getSomeOtherInstanceFromWhoKnowsWhere();
}
}
如果可能的话,将self()。不幸的是,由于接口方法是隐式公开的,所以只有当自类型的是一个抽象类时,这才可能实现
正如zhong.j.yu在评论中所说的那样,SELF
的绑定可能会被删除,因为它最终无法确保“SELF type”:
关键的一点是,QueryBuilder
不仅仅是一个平面实现,而是从复杂的构建器类层次结构扩展而来的“叶子”。同样的模式用于帮助者,如Where
、have
、或
等。所有这些都需要共享重要的代码
然而,你不应该忽视这样一个事实:所有这些最终只会带来语法上的甜头。一些有经验的程序员,或者至少。他们的担忧是合理的
总之,在实现它之前,仔细看看它是否真的有必要——如果有必要,就不要让它公开扩展。我发现了一个解决方案,它有点傻,但它可以工作:
在顶级课程(A)中:
我可能不完全理解这个问题,但仅仅这样做还不够吗(注意t):
私人静态类健美运动员{
私人最终整数高度;
私人最终串肤色;
//默认字段
私人浮动体脂=15;
私人整数权重=60;
公共健美运动员(整数高度,字符串颜色){
高度=高度;
这个。皮肤颜色=颜色;
}
公共T-setBodyFat(浮动体脂){
这个。体脂=体脂;
返回(T)这个;
}
公用T整定重量(内部重量){
重量=重量;
返回(T)这个;
}
公共机构建设(){
车身=新车身();
body.height=身高;
body.skinColor=皮肤颜色;
body.bodyFat=体脂;
体重=体重;
返回体;
}
}
这样子类就不必使用类型的重写或协方差来使母类方法返回对它们的引用
public class PersonBodyBuilder extends BodyBuilder<PersonBodyBuilder> {
public PersonBodyBuilder(int height, String color) {
super(height, color);
}
}
公共类PersonBodyBuilder扩展BodyBuilder{
公众人物健美运动员(整数高度,字符串颜色){
超级(高度、颜色);
}
}
通过@self注释为Java提供self类型
简单的情况是:
public class Foo {
public @Self Foo getMe() {
return this;
}
}
public class Bar extends Foo {
}
Bar bar = new Bar().getMe(); // Voila!
与泛型一起使用:
public class Tree {
private List<Tree> children;
public List<@Self Tree> getChildren() {
return children;
}
}
public class MyTree extends Tree {
}
MyTree tree = new MyTree();
...
List<MyTree> children = tree.getChildren(); // :)
公共类树{
私人名单儿童;
公共列表getChildren(){
返回儿童;
}
}
公共类MyTree扩展了树{
}
MyTree=新建MyTree();
...
List children=tree.getChildren();/:)
与扩展方法一起使用:
package extensions.java.util.Map;
import java.util.Map;
public class MyMapExtensions {
public static <K,V> @Self Map<K,V> add(@This Map<K,V> thiz, K key, V value) {
thiz.put(key, value);
return thiz;
}
}
// `map` is a HashMap<String, String>
var map = new HashMap<String, String>()
.add("bob", "fishspread")
.add("alec", "taco")
.add("miles", "mustard");
package extensions.java.util.Map;
导入java.util.Map;
公共类MyMapExtensions{
公共静态@Self Map add(@This Map thiz,K键,V值){
thiz.put(键、值);
返回thiz;
}
}
//'map'是一个HashMap
var map=newhashmap()
.添加(“bob”、“fishspread”)
.加上(“亚力克”、“塔可”)
.加上(“英里”、“芥末”);
我不确定OP是否希望A的foo
返回typeA
…只是扩展它的东西。看起来只有在B
的特定情况下,他才希望foo
返回aB
@Michael:这可能很适合OP的需要。如果他调用newa().foo()代码>,他应该有一个强类型的a
。如果他调用新的B().foo()代码>,他应该有一个强类型的B
。唯一缺少的是,A
的定义没有强制任何扩展它的B
必须从foo
返回AB
。否则,他应该使用递归泛型类型定义(必须..)。。
MyBaseClass() { }
abstract class SelfTyped<SELF> {
abstract SELF self();
}
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();
protected final <T> T a(T type) {
return type
}
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");
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;
}
}
public class PersonBodyBuilder extends BodyBuilder<PersonBodyBuilder> {
public PersonBodyBuilder(int height, String color) {
super(height, color);
}
}
public class Foo {
public @Self Foo getMe() {
return this;
}
}
public class Bar extends Foo {
}
Bar bar = new Bar().getMe(); // Voila!
public class Tree {
private List<Tree> children;
public List<@Self Tree> getChildren() {
return children;
}
}
public class MyTree extends Tree {
}
MyTree tree = new MyTree();
...
List<MyTree> children = tree.getChildren(); // :)
package extensions.java.util.Map;
import java.util.Map;
public class MyMapExtensions {
public static <K,V> @Self Map<K,V> add(@This Map<K,V> thiz, K key, V value) {
thiz.put(key, value);
return thiz;
}
}
// `map` is a HashMap<String, String>
var map = new HashMap<String, String>()
.add("bob", "fishspread")
.add("alec", "taco")
.add("miles", "mustard");