为什么Javatypes的参数不能有一个下界?

我收集你不能将Javagenericstypes参数绑定到下限(即使用super关键字)。 我正在阅读Angelika Langer Generics常见问题解答在这个问题上所说的话 。 他们说基本上归结为一个无用的下界(“没有任何意义”)。

我不相信。 我可以想象他们的使用,以帮助您更灵活的图书馆方法,产生一个打字结果的调用者。 想象一下,创build一个用户指定大小的数组列表并填充空string的方法。 一个简单的声明将是

 public static ArrayList<String> createArrayListFullOfEmptyStrings(int i); 

但这对您的客户来说是不必要的限制。 为什么他们不能像这样调用你的方法:

 //should compile List<Object> l1 = createArrayListFullOfEmptyStrings(5); List<CharSequence> l2 = createArrayListFullOfEmptyStrings(5); List<String> l3 = createArrayListFullOfEmptyStrings(5); //shouldn't compile List<Integer> l4 = createArrayListFullOfEmptyStrings(5); 

在这一点上,我会试图尝试下面的定义:

 public static <T super String> List<T> createArrayListFullOfEmptyStrings(int size) { List<T> list = new ArrayList<T>(size); for(int i = 0; i < size; i++) { list.add(""); } return list; } 

但它不会编译; super关键字在这种情况下是非法的。

我的例子上面是一个坏例子(忽略我下面说的)? 为什么不是一个下限在这里有用? 如果它有用,Java中不允许的真正原因是什么?

PS

我知道一个更好的组织可能是这样的:

 public static void populateListWithEmptyStrings(List<? super String> list, int size); List<CharSequence> list = new ArrayList<CharSequence>(); populateListWithEmptyStrings(list, 5); 

我们能否为这个问题的目的假装由于一个要求,我们需要在一个方法调用中做两个操作?

编辑

@Tom G(正当地)询问List<CharSequence>List<String>有什么好处。 首先,没有人说返回的列表是不可改变的,所以这里有一个好处:

 List<CharSequence> l2 = createArrayListFullOfEmptyStrings(5); l2.add(new StringBuilder("foo").append("bar")); 

基本上,它不够用。

我想你的例子指出了下限的唯一好处,FAQ称为Restricted Instantiation

底线是:所有“超级”的限制会买你限制 ,只有超types的数字可以用作types参数 。 ….

但正如其他post所指出的,甚至这个function的用处也是有限的。

由于多态性和专业化的性质,如常见问题解答( 访问非静态成员types擦除 )所述,上限比下限要有用得多。 我怀疑下界引入的复杂性是不值得的。


OP:我想补充我觉得你确实显示它是有用的,只是没有足够的用处。 拿出无可辩驳的杀手用例,我将支持JSR。 🙂

例如,规范确实讨论了types参数的下限

4.10.2

typesvariables是其下限的直接超types。

5.1.10

一个新的typesvariables…的下限

看起来,如果一个typesvariables是通配符捕获的合成结果,那么它只有一个(非空)下限。 如果语言允许所有types参数的下限呢? 可能不会造成很大的麻烦,只是为了保持generics的简单性而被排除在外(好吧…) 更新据说对低有界types参数的理论研究还没有彻底进行。

更新:声明下限的文章是可以的:“Javatypes的干扰:我们可以修复”Daniel Smith

RETRACT:以下参数是错误的。 OP的例子是合法的。

你特别的例子不是很有说服力。 首先它不是安全的。 返回的列表确实是一个List<String> ,将其视为另一个types是不安全的。 假设你的代码编译:

  List<CharSequence> l2 = createArrayListFullOfEmptyStrings(5); 

那么我们可以添加非string,这是错误的

  CharSequence chars = new StringBuilder(); l2.add(chars); 

那么List<String>不是,但有点像CharSequence的列表。 您的需求可以通过使用通配符来解决:

 public static List<String> createArrayListFullOfEmptyStrings(int size) // a list of some specific subtype of CharSequence List<? extends CharSequence> l2 = createArrayListFullOfEmptyStrings(5); // legal. can retrieve elements as CharSequence CharSequence chars = l2.get(0); // illegal, won't compile. cannot insert elements as CharSequence l2.add(new StringBuilder()); 

input列表给你什么好处呢? 当你遍历返回的集合时,你仍然应该能够执行以下操作:

 for(String s : returnedList) { CharSequence cs = s; //do something with your CharSequence } 

不止一个答案,这是另一个(可能是杀手?)的用例。 我有一个ModelDecorator帮手。 我希望它有以下的公共API

 class ModelDecorator<T>{ public static <T> ModelDecorator<T> create(Class<T> clazz); public <SUPER> T from(SUPER fromInstance); } 

所以,给定类A,B扩展A,可以这样使用:

 A a = new A(); B b = ModelDecorator.create(B.class).from(a); 

但是我想在T和SUPER上有界限,所以我确保只有子类可以使用API​​实例化。 在这一刻,我可以做到:

 C c = new C(); B b = ModelDecorator.create(B.class).from(c); 

B不从Cinheritance

显然,如果我可以这样做:

  public <SUPER super T> T from(SUPER fromInstance); 

这将解决我的问题。

嗯,好的 – 让我们来看看这个。 你定义一个方法:

public static <T super String> List<T> createArrayListFullOfEmptyStrings(int size) {

这意味着什么? 这意味着,如果我打电话给你的方法,那么我得到一个string的超类列表。 也许它返回一个String列表。 也许它返回一个Object列表。 我不知道。

凉。

List<Object> l1 = createArrayListFullOfEmptyStrings(5);

据你说,这应该编译。 但是这是不对的! 我可以把一个Integer放到Object – l1.add(3)的列表中。 但是,如果你正在返回一个String列表,那么这样做应该是非法的。

List<String> l3 = createArrayListFullOfEmptyStrings(5);

据你说,这应该编译。 但是这是不对的! l3.get(1)应该总是返回一个string…但是该方法可能已经返回了一个Object列表,这意味着l3.get(1)可以想象为一个Integer。

唯一的作品是

List<? super String> l5 = createArrayListFullOfEmptyStrings(5);

我所知道的是,我可以安全地调用l4.put("foo") ,并且我可以安全地获得Object o = l4.get(2)