二阶泛型似乎与一阶泛型不同 [英] Second order generics seem to behave differently than first order generics

查看:85
本文介绍了二阶泛型似乎与一阶泛型不同的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我认为我对泛型有合理的把握。例如,我明白为什么

  private void addString(List< ;? extends String> list,String s){
list.add(一个或多个); //不会编译
list.add(list.get(0)); //不能编译
}

不编译。 我甚至通过知识获得了一些互联网业务



但是我认为这是不应该编译的:

  private void addClassWildcard(List< Class< ;? extends String> > list,Class< ;? extends String> c){
list.add(c);
list.add(list.get(0));
}

也不应该这样:

  private void addClass(List< Class< ;? extends String>> list,Class< String> c){
list.add(c);
list.add(list.get(0));
}

但是都编译。为什么?与上面的例子有什么不同?



我希望能够对普通英语的解释以及指向Java规范或相似部分的指针。第二种情况是安全的,因为所有类的实例 >是类的实例<?扩展字符串>



添加一个 Class< ;?实例并不安全。将String> 扩展为 List< Class< ;? extends String> - 您将返回一个类的实例< ;?使用 get(int) iterator()等扩展String>




从某种意义上讲,只考虑 Class 中的通配符当一个实例遇到时。考虑以下示例(从字符串转换为 Number ,因为字符串

  private void addClass(List< Class< ;? extends Number>> list,Class< Number> c ){
list.add(c);
list.add(list.get(0));
}

private void tryItSubclass(){
List< Class< Integer>> ints = new ArrayList<>();

addClass(ints,Number.class); //不会编译
}

这里 ints 只能包含 Class Number.class 的实例也是类< ;?使用捕获为 Number 扩展Number> ,这两种类型不兼容。 / p>

  private void tryItBound(){
List< Class< Number>> ints = new ArrayList<>();

addClass(ints,Number.class); //不会编译
}

这里 ints 只能包含 Class Integer.class 的实例也是类< ;?使用捕获为整数扩展Number> ,这两种类型不兼容。 / p>

  private void tryItWildcard(){
List&
addClass(ints,Number.class); //不会编译

Class< ;?扩展Number> aClass = ints.get(0);

$ / code>






第一种情况是不安全的因为 - 是否有一个假设的类扩展了 String (没有,因为 String final ;但是,泛型忽略 final ),a List< ;? extends String> 可能是 List< HypotheticalClass> 。因此,您不能将字符串添加到列表< ;?因为您希望列表中的所有内容都是 HypotheticalClass 的实例:

 列表与LT; HypotheticalClass> list = new ArrayList<>(); 
列表< ;? extends String> list2 = list;
list2.add(); //不允许,但假装是。
HypotheticalClass h = list.get(0); // ClassCastException。


I thought I have a reasonable grasp of generics. For example, I understand why

private void addString(List<? extends String> list, String s) {
    list.add(s); // does not compile
    list.add(list.get(0)); // doesn't compile either
}

Does not compile. I even earned some internet karma with the knowledge.

But I'd think by the same argument this shouldn't compile:

private void addClassWildcard(List<Class<? extends String>> list, Class<? extends String> c) {
    list.add(c);
    list.add(list.get(0));
}

Nor should this:

private void addClass(List<Class<? extends String>> list, Class<String> c) {
    list.add(c);
    list.add(list.get(0));
}

But both compile. Why? What is the difference to the example from the top?

I'd appreciate an explanation in common English as well as a pointer to the relevant parts of the Java Specification or similar.

解决方案

The second case is safe because all instances of Class<String> are instances of Class<? extends String>.

There is nothing unsafe about adding an instance of Class<? extends String> to a List<Class<? extends String> - you will get back an instance of Class<? extends String> using get(int), iterator() etc - so it's allowed.


In a sense the wildcard inside Class gets only considered when an instance of that is actually encountered. Consider the following examples (switching from String to Number since String is final).

private void addClass(List<Class<? extends Number>> list, Class<Number> c) {
    list.add(c);
    list.add(list.get(0));
}

private void tryItSubclass() {
    List<Class<Integer>> ints = new ArrayList<>();

    addClass(ints, Number.class); // does not compile 
}

Here ints can only ever contain instances of Class<Integer> but Number.class is also a Class<? extends Number> with ? captured as Number so the two types are not compatible.

private void tryItBound() {
    List<Class<Number>> ints = new ArrayList<>();

    addClass(ints, Number.class); // does not compile
}

Here ints can only ever contain instances of Class<Number> but Integer.class is also a Class<? extends Number> with ? captured as Integer so the two types are not compatible.

private void tryItWildcard() {
    List<Class<? extends Number>> ints = new ArrayList<>();

    addClass(ints, Number.class); // does compile

    Class<? extends Number> aClass = ints.get(0);
}


The first case is unsafe because - were there a hypothetical class which extended String (which there isn't, because String is final; however, generics ignore final), a List<? extends String> might be a List<HypotheticalClass>. As such, you can't add a String to a List<? extends String>, because you expect everything in that list to be an instance of HypotheticalClass:

List<HypotheticalClass> list = new ArrayList<>();
List<? extends String> list2 = list;
list2.add("");  // Not allowed, but pretend it is.
HypotheticalClass h = list.get(0);  // ClassCastException.

这篇关于二阶泛型似乎与一阶泛型不同的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆