在处理Java项目时遇到了一件很奇怪的事情。
我在interface
中有此基本方法:
public interface Registry<T extends FlakeType<? extends F>, F extends Flake> {
@Nullable
public <E extends F> T findType(@Nonnull String name);
}
很显然,类型参数E
完全没有用,因为它既不在返回类型中也不在参数中使用。我的IDE也会通知我。
我还有另一个扩展Registry
的接口:
public interface EntityRegistry extends Registry<EntityType<? extends Entity>, Entity> {
@Nullable
@Override
<E extends Entity> EntityType<E> findType(@Nonnull String name);
}
对于上下文,Entity
扩展了Flake
,EntityType<E extends Entity>
扩展了FlakeType<E>
。
如您所见,我正尝试将此方法的返回类型从EntityType<? extends Entity>
(此类型来自T
类型参数)指定为EntityType<E>
,而{{1 }}是方法类型参数E
。
从逻辑上讲,<E extends Entity>
和? extends Entity
在这种情况下具有相同的含义,因为E extends Entity
没有其他限制或类似限制,因此从本质上讲,它还充当类型扩展的通配符E
。
这很好用。
但是,我注意到我的IDE(Intellij)发出的通知
从未使用过类型参数
Entity
。
这使我认为我可以删除它,因为它显然没有用,并且与返回值或任何参数都没有任何关系。
重载方法提供了自己的E
类型参数,该类型参数绝不应与基本方法中同名的无用类型参数有关。 还是有?
因为我将基本方法更改为此:
E
以相同的方式覆盖它不再是可能的:
@Nullable public T findType(@Nonnull String name);
中的
findType(String)
与EntityRegistry
中的findType(String)
;两种方法的擦除相同 都不会覆盖另一个。
我的问题是:
为什么Java在基本方法中需要无用的类型参数? 一个覆盖方法想指定返回值的泛型类型?覆盖方法提供的参数不足够吗?
为什么在第二种情况下覆盖无效?它不起作用是因为签名与第一种情况完全相同,只是使用了另一个类型参数代替显式通配符,但是由于这确实意味着同一件事,所以有什么区别?
我确定我对泛型做出了一些错误的假设,这就是为什么对此感到困惑的原因。你能帮我清理一下吗?
谢谢!
答案 0 :(得分:2)
类型参数是方法签名的一部分。您可以在JLS §8.4.2中找到它:
两个方法或构造函数M和N,如果它们具有相同的名称,相同的类型参数(如果有的话)(§8.4.4),并且具有相同的名称,则具有相同的签名。 N的形式参数类型与M的类型参数相同,形式参数类型相同。
方法m1的签名是方法
m2
的签名的子签名,如果有以下情况:
m2
与m1
具有相同的签名,或者
m1
的签名与m2
的签名的擦除(§4.6)相同。如果
m1
是m2
或m1
的子签名,则两个方法签名m2
和m2
是等效的是m1
的子签名。
在Registry
中没有类型参数的情况下,EntityRegistry
中的方法没有基本接口方法的子签名,因此不被视为替代。
答案 1 :(得分:1)
请参阅answer by Jorn Vernee,以获取有关为何无法编译的完整说明:
类型参数是方法签名的一部分。
要使其编译,只需在@Nullable
@Override
EntityType<? extends Entity> findType(@Nonnull String name);
中将方法指定为:
E
这与原始版本一致,因为?
实际上是未定义的,即if ($('h3').text().includes('®')) {
// Do stuff
}
。
答案 2 :(得分:1)
有效覆盖-两个接口中findType
方法的签名相同:
<E>findType(String name)
无效的替代-由于移除了类型参数E
,因此签名变得不同:
<E>findType(String name)
findType(String name)
答案 3 :(得分:1)
原始代码问题的很大一部分是不必要的复杂。另外,如果不解决未经检查的强制转换,就无法实现原始的 EntityRegistry.findType()
。
...warning: [unchecked] unchecked cast
return ( EntityType< E > ) new EntityType< >( new Entity( name ) );
^
required: EntityType<E>
found: EntityType<Entity>
where E is a type-variable:
E extends Entity declared in method <E>findType(String)
1 warning
更糟糕的是,因为 EntityRegistry.findType()
是一种通用方法it can't be used as a lambda。
这里是a much simpler, more type-safe solution:
interface Registry< T extends FlakeType<? extends Flake> > {
T findType(String name);
}
interface EntityRegistry< U extends EntityType<? extends Entity> > extends Registry<U> {
@Override
U findType(String name);
}
上面的链接中成功编译并正确运行的实现以这种方式工作...
...
EntityRegistry<EntityType<Entity>> registry = (name) -> { return new EntityType<Entity>(new Entity(name)); };
...
以这种方式...
...
Registry<EntityType<Entity>> registry = (name) -> { return new EntityType<Entity>(new Entity(name)); };
...