感觉是有点问题,像是设计上的问题。
People p=new People();
List extends User> list=new ArrayList
list.add(p);
像你这个代码,如果list.add(p);加的不是一个People,那会怎么样呢?假设另一个类Man继承于User,且Man与People没从属关系。那么然后按照List extends User> list来理解,将Man加到list里面应该是没问题的吧。可是另一方面我们定义的list是一个new ArrayList
而按照freish的说法也是不那么对的样子。因为如果存在这么一个函数f(List extends Number> LN),在这里函数里,我们限制了输入的参数LN必须只能存放数字类Number,这样一个限制应该是很有用的吧,可以避免你把一个List
在你这个问题里面的话,把list写出 List extends User> list 是没什么实际意义的,但是如果像是在上面函数f(List extends Number> LN)里的话确是有意义了。也就是说理论上来说,通配符是被设计成一个有用的东西的,也就是用来限定传入函数的参数的(或者赋值时来限定等号右边的)。而通过通配符来限定是某某某的子类或者超类在list这种容器应用的时候却产生了矛盾,也就是上面说的你本来只是想把People给加到list的,结果却一不小心把人家Man给加进来了。这样看来的话,java应该是考虑到这种情况了,所以就? extends User 成什么乱七八糟的 capture#105 of ? extends User类型了。泛型产生的原因就是当时的list这些容器能把任何类型的东西存进去又再拿出来(因为是Object),这样容易产生混乱。所以就产生了泛型来限制和检查类型。而这种限制却在这些特殊的情况下面产生了矛盾。
嗯,所以总结来说,你不能这么写代码,不能把要操作到泛型参数的方法的类声明为带有通配符的模式。还有一些例子看起来好似能通过编译的,实际确是不行的。比如说
class People
void f(K k){System.out.println("f(K k)");}
void f(V v){System.out.println("f(V v)");}
}
在这里f有着两个不同类型参数K和V,看起来好像没事儿的样子。但是这是不能编译成功的。因为如果K是String而V也是String的时候,那么两个函数不就冲突了么?
以下是我自己学习通配符部分的学习笔记,希望能对你有帮助:
在上一篇中说到,泛型不具有内建的协变类型,因此,这样是无法通过编译的:
List
这是因为List
List extends Fruit> fruit = new List
这样,就可以可以通过编译器的检查了。在这里,List extends Fruit>实际上并不是指这个List可以持有任何类型的
Fruit,通配符引用的是明确的类型,所以它的意思是某种指定的具体类型,所以编译器只是知道它可能是持有Apple的
List,也可能是持有Orange的List,或者其他持有其他具体类型的Fruit的List,所以上面的可以通过,然而正是这种不确定性导致了一些不愉快的情况发生:
fruit.add(new Apple()) ; //compile-time error
fruit.add(new Fruit()) ; //compile-time error
奇怪啊!怎么把它们放进去都是出错呢?我想可能是这样,既然fruit是持有某种具体类型的List,但是编译器并不知道是哪种,你随便放一个进去,是正确的吗?这一点连编译器都无法判断,怎么能随意就通过编译器的检查呢?于是就出错了。但是如果你真的用一种神奇的方法(事实证明这种神奇的方法是存在的)将某个对象加进去,试图想将它取出时,编译器知道它至少是一个Fruit,所以你可以这样是行得通的:
Fruit f = fruit.get(0) ;
其实这个无法通过编译的本质原因是Java设计者在捣鬼,查看List的源代码,会发现add的方法是这样的:
boolean add(E e) ;
在这里(以上代码是我从源代码中复制过来的)E是代表泛型参数,所以当E是 extends Fruit>的时候,调用add方法时就要通过检查,因此无法通过。如果参数类型是Object,这样无论什么类型都是Object,所以就可以调用了,比如:
boolean contains(Object o) ;
boolean remove(Object o) ;
以下代码是个简单的应用举例:
LIst extends Fruit> fruit = Arrays.asList(new Apple()) ;
fruit.contains(new Apple()) ;
fruit.indexOf(new Apple()) ;
fruit.remove(new Apple()) ;
通配符的另一个应用是与super结合:List super T> 。顾名思义,这是持有任何T的基类的容器。那样是不是所有T的基类都可以放进去呢?同样编译器是无法知道是哪个具体的T的基类,所以无法检查,所以编译器就不能确保安全性,所以就无法通过。但是无论是哪一个具体的基类,但是如果是放入T或者是T的基类都是可以的,因为它们可以安全的向上转型而不会出现任何差错。
还有一种通配符的用法是“?”的孤军奋战:List>。这个我也可以来一次顾名思义,就是持有任何类型的容器,但是限于某种具体的。然而你会发现,如果没有使用泛型,只有List,它也是可以持有任何类型的,所以编译器一般不会太在意这点区别。但是还是有另一种特殊情形:List extends Object>,这就相当有趣,它表示持有Object或从Object继承下来的类的容器,但是在Java中,所有的类都是从Object中继承的,所以它的范围和上面两种情况一概一样的啊!可是在
List和List extends Object>的相互赋值是会出现警告,但是List>和List extends Object>的相互赋值是不会的。这样一种有趣的情形就会出现,如果将List extends Object>和List>相互赋值,再将List>和List相互赋值,这样就实现了 List>和List extends Object>的相互赋值,且不会出现警告,看以下应用:
public class ExchangeValue{
static
static void f2 ( List> list ) { f1(list) ;}
public static void main (String[] args){
List ls = new List
f1(ls) ; //warning!!
f2(ls) ; //no warning!!
}
泛型可以用"
解释: “
List
List
可以赋值给list:list.add("StringBatch");
可以获取到list的值:list.get(0),结果就是”StringBatch“;
这个时候T的类型也是String。也就是说T是动态的,可以被任意指定类型。
规则:
如果你使用了“? extends T”,你不能往该数据结构中add元素,但可以get元素。
换句话说,如果你要在list中加入User及其子类,用得着通配符么?!
直接List
List extends User>你这么写java好像不知道你的list到底是什么类型的东西,所以报错!!!
推荐这样写List
泛型是强类型,用之前应该为它指定类型 省去了转型的烦恼!!!