假设我们有三个类-AbstractMessage
,AbstractEngine
和AbstractAction
。这三个类都以通用方式互相引用,因此每个引擎都有对应的消息和操作,您可以在代码中直接引用它们。
public class MyMessage<M extends AbstractMessage<M,E,A>, E extends AbstractEngine<M,E,A>, A extends AbstractAction<M,E,A>> {
这很好,但是当我尝试以最高级别实施行为时,我遇到了一些问题。我的AbstractAction类具有一个由此定义的
applyTo
方法:protected abstract M applyTo(E engine, Object guarantee);
我的AbstractEngine类有这个
private final M apply(A action) {
return action.apply(this, this.guarantee);
}
正是在这条线上,它令人沮丧-抱怨:
The method applyTo(E, Object) in the type AbstractAction<M,E,A> is not
applicable for the arguments (AbstractEngine<M,E,A>, Object)
现在,这样做的原因很明显-所讨论的E可能是其他OTHER AbstractEngine,并且无法知道我们从中调用的子类是否实际上是
E
。我的问题是,我如何确定地说如果您要使用
class MyEngine extends AbstractEngine<M...,E...,A...>
,那么MyEngine
是,必须是E
?并将这种确定性转化为AbstractEngine
吗?这是一个说明问题的小例子。
class EngineExample {
static abstract class AbEng<A extends AbAct<A,M,E>, M extends AbMes<A,M,E>, E extends AbEng<A,M,E>> {
final M func(A act) {
return act.apply(this); // compile error here
}
}
static abstract class AbMes<A extends AbAct<A,M,E>, M extends AbMes<A,M,E>, E extends AbEng<A,M,E>> {
}
static abstract class AbAct<A extends AbAct<A,M,E>, M extends AbMes<A,M,E>, E extends AbEng<A,M,E>> {
abstract void apply(E e);
}
static class RealEng extends AbEng<RealAct, RealMes, RealEng> {
}
static class RealMes extends AbMes<RealAct, RealMes, RealEng> {
}
static class RealAct extends AbAct<RealAct, RealMes, RealEng> {
void apply(RealEng eng) {
System.out.println("applied!");
}
}
}
最佳答案
使用最宽松的有效参数类型
最简单的解决方案是不实际执行this isInstanceOf E
。抽象规则已经保证了此操作是安全的,因此,如果您将参数更改为仅允许任何Engine,则它将起作用。
abstract Action<E> {
public void apply(Engine<?> e, Object o) {
e.doSomething(o);
}
}
要么
abstract Action<E> {
<T extends Engine<?>> public T apply(T e, Object o) {
return e.doSomething(o);
}
}
使用类型安全的包装器
另一个解决方案是创建将这3个绑定在一起的另一个类,并将交互调用移至包装器。
abstract System<A extends Action, M extends Message, E extends Engine> {
abstract void apply(A action, E engine) {
engine.render(action.apply())
}
}
或者让包装器类使用这3个实例,并使用传入的版本。基本上,这是“允许任何东西足够接近”的解决方案,并添加了另一个类来管理彼此之间如何交谈。
预制检查
如果强制转换设置无效,您还可以对构造进行参考强制转换以引发错误。
private final E dis = (E) this;
这实际上只是将问题从始终在编译时转移到了有时在运行时,所以通常,这不是一个安全/稳定的解决方案。
下一个解决方案针对您的情况(使用我们讨论中的信息)。基本上,您想在抽象类中定义一个方法,类A和B可以继承该方法,但是A和B不应使用其基类互换。
只需使用多态性并将泛型用作类型分隔符
这是对MVCe的修改,改用多态性,仅将泛型用作一种类型类别排他锁机制。基本上,Type是一个语义接口,用于说明是否在语义上使这些类相互交谈是有意义的。 (物理引擎和光引擎可能共享某些功能,但是让它们可以互换是没有意义的。)
class test {
public static void main(String[] rawrs) {
RealEng re = new RealEng();
RealAct ra = new RealAct();
MockAct ma = new MockAct();
ra.apply(re);
// Remove all code related to Type interface if next line should compile
ma.apply(re); // compile error here
}
static interface Type {
}
static interface Real extends Type {
};
static interface Mock extends Type {
};
static abstract class AbEng<T extends Type> {
final void func(AbAct<T> act) {
act.apply(this); // compile error here
}
}
static abstract class AbMes<T extends Type> {
}
static abstract class AbAct<T extends Type> {
abstract void apply(AbEng<T> e);
}
static class RealEng extends AbEng<Real> {
}
static class RealMes extends AbMes<Real> {
}
static class RealAct extends AbAct<Real> {
@Override
void apply(AbEng<Real> eng) {
System.out.println("applied!");
}
}
static class MockAct extends AbAct<Mock> {
@Override
void apply(AbEng<Mock> eng) {
System.out.println("applied!");
}
}
}