为什么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)
。