我需要提供一个在单独的类层次结构中使用的通用接口,并希望该接口支持调用链。
我试图使用反射边界来做到这一点,但我似乎无法将其投入使用而无需投射"这个"到所需的类型。这是我目前的做法:
public interface Operable<T extends Operable<T>> {
T prepare();
T operate();
}
public abstract class BaseOperable<T extends Operable<T>> implements Operable<T> {
@Override
public T prepare() {
System.out.println("Preparing...");
// why is this needed? "this" is a BaseOperable that implements Operable<T>
return (T) this;
}
}
public class SpecialOperable<T extends SpecialOperable<T>> extends
BaseOperable<SpecialOperable<T>> {
@Override
public T operate() {
System.out.println("Operation "
+ (Math.random() > 0.5 ? "succeeded" : "failed"));
// this seems to be required
return (T) this;
}
@Override
public T prepare() {
// if I don't override this, the supertype T is used which is
// BaseOperable and hides "specialOp" from chaining
return (T) super.prepare();
}
public T specialOp() {
System.out.println("Doing something special...");
return (T) this;
}
}
以上以下代码行编译:
mySpecialOperable().prepare().operate().specialOp().operate();
我的问题是:有没有办法避免每个return语句的类型转换?是否也可以不必覆盖最专业级别的所有内容(如prepare()
方法所做的那样)?
答案 0 :(得分:0)
您的问题可以简化为:
public abstract class AbstractOperable<T extends AbstractOperable<T>> {
public T prepare() {
// Type mismatch: cannot convert from AbstractOperable<T> to T
return this;
}
}
public class OperableImpl extends AbstractOperable<OperableImpl> {
}
现在,请考虑以下类:
public class OperableImplHack extends AbstractOperable<OperableImpl> {
}
虽然它符合T extends AbstractOperable<T>
合同,但this
不是T
。这就是编译器无法知道this
中T
是否AbstractOperable
的原因。
解决方案#1:
public abstract class AbstractOperable<T extends AbstractOperable<T>> {
public abstract T getThis();
public T prepare() {
return getThis();
}
}
public class OperableImpl extends AbstractOperable<OperableImpl> {
@Override
public OperableImpl getThis() {
return this;
}
}
解决方案#2:
public abstract class AbstractOperable<T extends AbstractOperable<T>> {
protected T that;
public T prepare() {
return that;
}
}
public class OperableImpl extends AbstractOperable<OperableImpl> {
public OperableImpl() {
that = this;
}
}
答案 1 :(得分:0)
问题在于,如果转换是合法的,那么您在编译器无法在编译时确定的地方假设类型安全。这需要你遇到的警告:为了使这个更清楚,假设:
class Foo implements Operable<Foo> { ... } // legal
class Bar implements Operable<Foo> { ... } // not intended, but also legal
Bar
类不是您的抽象模型合法的。您希望实现自我类型,但在Java中您可以实际要求的是要求实现给定接口的类型T
的扩展。因此,延伸
class Bar extends BaseOperable<Foo> { ... }
基本上会在运行时执行,就好像你实现了:
class Bar implements Operable<Foo> {
@Override
public Foo prepare() {
System.out.println("Preparing...");
// Why is this needed, you ask? Because you are now expressing this:
return (Foo) this; // this is of type Bar, not Foo
}
...
}
其中this
是Bar
的实例,而不是Foo
的实例。当然,这会导致ClassCastException
,并且对于这种可能性,编译器会警告您静态类型安全性已经失败,并且此异常可能发生在您通常不期望的情况下。
您通常可以通过添加以下方法来避免这种情况:
protected abstract T self();
然后由任何非抽象类实现。由于此方法是使用非泛型返回类型实现的,因此静态编译检查可以完成其工作,并禁止任何非法返回类型,如我们之前观察到的那样。然后,上面的prepare
方法将实现如下:
public T prepare() {
System.out.println("Preparing...");
return self();
}
但是,如果您正在使用未由任何用户实现的封闭式API,并且您希望采用该快捷方式(希望您具有针对任何可能的滥用行为进行验证的单元测试),则可以使用{{注释该方法1}}告诉编译器你知道你正在处理什么。