Java 如何确保这是类签名中引用的泛型类型?
假设我们有三个类-Java 如何确保这是类签名中引用的泛型类型?,java,generics,Java,Generics,假设我们有三个类-AbstractMessage,AbstractEngine,和AbstractAction。这三个类都以通用方式相互引用,因此每个引擎都有相应的消息和操作,您可以在代码中直接引用它们 public class MyMessage<M extends AbstractMessage<M,E,A>, E extends AbstractEngine<M,E,A>, A extends AbstractAction<M,E,A>> {
AbstractMessage
,AbstractEngine
,和AbstractAction
。这三个类都以通用方式相互引用,因此每个引擎都有相应的消息和操作,您可以在代码中直接引用它们
public class MyMessage<M extends AbstractMessage<M,E,A>, E extends AbstractEngine<M,E,A>, A extends AbstractAction<M,E,A>> {
我的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)
Java泛型中的递归类型参数通常很麻烦 这里的问题是,矛盾的是,你无法保证
这个引用了E
的一个实例;关于这个
我们知道的唯一一件事是它还扩展了引擎
,但实际上它不是E
显而易见的解决方案是添加一个cast((E)this
),这可能是一个可接受的解决方案,但您必须在合同(通过javadoc或其他文档)中明确指出,引擎
扩展类必须将E
分配给它们自己
另一种解决方案只是将这些方法签名更改为更灵活一点,而不是E
接受扩展engine
的任何引擎
这样做的原因是,当我们声明MyEngine
时,编译器确实知道MyEngine
是E
,因此“cast”是安全的。然后,AbstractEngine
中的代码可以安全地使用铸造值
明显的不便之处在于额外的字段引用了这个
,虽然在实践中有一点内存浪费,但可能可以忽略不计
在这里,我们添加了一种可能性,即引擎可以指定一个代理引擎,用于它们的apply
方法调用。也许这会有用。。。但是如果您真的想让第三个引擎无法在这里使用,那么您可以在AbstractEngine
构造函数中更改代码,将传递的引擎与this
进行比较,如果它们不相同,则在运行时失败
protected AbstractEngine(final E engine) {
if (engine != this) {
throw new IllegalArgumentException();
}
this.engine = engine;
}
不幸的是,这不能在编译时检查。。。您可以做的第二件最好的事情是将其作为代码测试的一部分,以验证所有AbstractEngine
扩展类是否兼容,以便在构建时失败。使用最宽松的有效参数类型
最简单的解决方案是,不要强制执行这是E的替代。抽象规则已经保证了这是一个安全的操作,因此,如果您将参数更改为只允许任何引擎,那么它将正常工作
abstract Action<E> {
public void apply(Engine<?> e, Object o) {
e.doSomething(o);
}
}
这实际上只是将问题从总是在编译时转移到有时在运行时,因此通常不是一个安全/稳定的解决方案
下一个解决方案有点特定于您的案例(使用我们讨论中的信息)。基本上,您希望在抽象类中定义一个类a和B可以继承的方法,但是a和B不应该使用它们的基类进行互换
只需使用polymophism,并将泛型用作类型分离器
这里是对MVCe的一个修改,它使用多态性,仅将泛型用作一种类型类别独占锁定机制。基本上,类型是一个语义接口,用于说明这些类之间的对话在语义上是否有意义。(物理引擎和光引擎可能共享一些功能,但让它们互换是没有意义的。)
类测试{
公共静态void main(字符串[]rawrs){
RealEng re=新RealEng();
RealAct ra=新RealAct();
MockAct ma=新MockAct();
ra.申请(再申请);
//若下一行需要编译,请删除所有与类型接口相关的代码
ma.apply(re);//此处编译错误
}
静态接口类型{
}
静态接口实扩展类型{
};
静态接口模拟扩展类型{
};
静态抽象类标记{
最终无效职能(AbAct法案){
act.apply(this);//此处编译错误
}
}
静态抽象类AbMes{
}
静态抽象类AbAct{
摘要无效申请(AbEng e);
}
静态类RealEng扩展了AbEng{
}
静态类领域扩展了AbMes{
}
静态类RealAct扩展了AbAct{
@凌驾
无效申请(阿本英语){
System.out.println(“应用!”);
}
}
静态类MockAct扩展了AbAct{
@凌驾
无效申请(阿本英语){
System.out.println(“应用!”);
}
}
}
引擎本身不需要自己的类型,但它需要知道消息属于该引擎,而不是其他引擎。我尝试过其他的变化,最终总是需要所有三对所有三。就像你说的,泛型总是麻烦。至于你的解决方案,我目前正在铸造它。但我宁愿不去。我宁愿它按我想要的方式工作。至于你的第二个解决方案,更加灵活与我所希望的正好相反。我想要它和那个引擎一起,你是指引擎类型还是实例?如果是后者,则始终需要使用引擎的引用,并在运行时进行验证。有趣的一点。当我发表评论时,我的本意是前者。但是我想得越多,我就越意识到,因为这些引擎不是单例(在一台服务器上肯定会有多个相同引擎类型的实例运行),所以我必须考虑这个问题的答案。尽管如此,我还是觉得这个
(E)的演员阵容有点老套,我想避免。不过,您的问题很重要,可能会使事情复杂化。@corsika要使E保留在applyTo中并避免每次强制转换,您可以做的是在AbstrsctEngine中声明一个字段,该字段由构造函数设置为.final E,该构造函数将is值作为参数,具体引擎将自己传递给该构造函数。将上升
protected abstract M applyTo(AbstractEngine<M, A, E> engine, Object guarantee);
public class AbstractEngine<M extends ..., A extends ..., E extends ...> {
private final E engine;
protected AbstractEngine(final E engine) {
this.engine = Objects.requiresNonNull(engine);
}
}
public class MyEngine extends AbstractEngine<MyMessage, MyAction, MyEngine> {
public MyEngine() {
super(this);
}
}
protected AbstractEngine(final E engine) {
if (engine != this) {
throw new IllegalArgumentException();
}
this.engine = 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);
}
}
abstract System<A extends Action, M extends Message, E extends Engine> {
abstract void apply(A action, E engine) {
engine.render(action.apply())
}
}
private final E dis = (E) this;
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!");
}
}
}