我在理解Java泛型方面遇到了问题,我已经简化为这个例子
class A<T extends B> {
public void fun(T t) {
}
}
class B {
A a;
public void event() {
a.fun(this);
}
}
问题是这会产生警告,因为A在B中定义但A已经将其用作泛型类型。
我的第一直觉是我的设计错了,但在这种情况下我无法改变它。 A就像一个集合,B就像集合中的一个节点,用户应该覆盖它。某些事件可能发生在B中,需要向父母A报告。
但由于A一般用B定义,我如何避免B.event()中的编译警告
由于
答案 0 :(得分:13)
问题是你在这一行上使用的是原始类型:
A a;
您需要为A的类型参数(T)指定类型。
你可以这样做:
A<B> a;
但是如果我理解你对这个问题的陈述,那么A根本不是通用的。你可能想做这样的事情:
class A<T> {
public void fun(T t) {
}
}
class B<T extends B<T>> {
A<B<T>> a;
public void event() {
a.fun(this);
}
}
甚至是这样:
class A<T extends B<? extends T>> {
public void fun(T t) {
}
}
class B<T extends B<T>> {
A<? super B<T>> a;
public void event() {
a.fun(this);
}
}
这些之间存在一些可能有用的变化。后一个例子是最通用的(但显然也是最复杂的)。
class A<T extends B<? extends T>>
确保A的类型参数是B.因为B本身是通用的,并且具有该循环类型参数,所以最终需要说B<? extends T>
(简单地说T赢了)在这里工作。
class B<T extends B<T>>
尽可能接近在Java中模拟“自我类型”。这让B谈论自己的(几乎)具体的子类型。当继承B时,你会说“class C extends <B<C>>
”之类的东西。这很有用,因为现在C.a
的类型实际上是A<? super B<C>>
。
后一个示例中的? super
位仅在您计划将B与A
连接时才有用,该B
不是完全相同类型的A<Shape>
。用具体的方式思考,假设你有一个Circle
和一个Shape
(扩展B
扩展A<Circle>
)。超级通配符允许您一起使用它们。没有它,您需要A<Shape>
而不是Circle
{{1}}。
答案 1 :(得分:11)
<强>代码强>
public class A<T extends B> {
public void fun(T t) {
}
}
public class B {
A<B> a;
public void event() {
a.fun(this);
}
}
警告被征服了。
<强>原因强>
类型A
的变量应使用特定的类类型声明,如泛型类签名(A<T extends B>
)所示。< / p>
解决强>
虽然这解决了编译器警告,但仍存在潜在问题。劳伦斯为核心问题提供了出色的解释和解决方案。