我希望能实现以下目标:
public class ImmutableList<T> {
public <U super T> ImmutableList<U> add(U element) { ... }
}
也就是说,假设有一个不可变的
T
列表,您可以向列表中添加任何 U
,从而产生一个不可变的 U
列表,但有一个约束条件,即 U
必须是 T
的超类型。例如:
- 我可以将一只猴子添加到猴子列表中,得到一个新的猴子列表;
- 我可以将一个人添加到猴子列表中,得到一个新的类人猿列表(假设猴子和人的最小上界是类人猿);
- 我可以将一块石头添加到类人猿列表中,得到一个新的
Object
列表(假设石头和类人猿没有其他共同祖先)。
U
的下界是不合法的。我可以改为写成:public class ImmutableList<T> {
public ImmutableList<T> add(T element) { ... }
public static <U> ImmutableList<U> add(ImmutableList<? extends U> list, U element) { ... }
}
通过这种方式,编译器将正确地推断出列表元素类型与我们要添加的 U
之间的最小上界。这是合法的。但它并不好用。比较如下:
// if 'U super T' were legal
list.add(monkey).add(human).add(rock);
// assuming 'import static ImmutableList.add'
add(add(add(list, monkey), human), rock);
我非常喜欢函数式编程,但我不希望我的Java代码看起来像是Lisp方言。所以我有三个问题:
什么鬼? 为什么绑定类型参数下限
<U super T>
是不合法的?这个问题之前已经在此处提出过 ("为什么Java类型参数不能有下限?"),但我认为那里的表述有些混乱,那里的答案归结为“不够有用”,我并不是很信服。JLS 第4.5.1节指出:“与在方法签名中声明的普通类型变量不同,在使用通配符时不需要进行类型推导。因此,在通配符上声明下限是允许的。”考虑到上面的另一种
static
方法签名,编译器显然能够推断出它需要的最小上限,所以这个争论似乎是错误的。有人可以反驳吗?最重要的: 有人可以想到一个合法的替代我的带下限方法签名的方法吗?简而言之,目标是让调用代码看起来像Java (
list.add(monkey)
)而不是Lisp (add(list, monkey)
)。
Class<U> clazz
)。没有其他方法可以编写正确返回推断类型列表的代码。 - Ted Hopp