当前位置:编程学习 > JAVA >>

无限制的通配符List<?>

最近在effective java中看到无限制通配符,没理解,特发此帖,向高手讨教一下:
比如 
void test(List<?> list){}

书中说的是,不能把null之外的任何值放入List<?>中。
1.这是什么意思?只能放null值?
2.我试着写以下代码,也没出错,是我理解错了?
List list = new ArrayList(); list.add(1); swap(list);

3.这么写的作用是什么? --------------------编程问答-------------------- List<?> list代表list里面在编译期检查时可以放任意类型 --------------------编程问答--------------------
引用 1 楼 suciver 的回复:
List<?> list代表list里面在编译期检查时可以放任意类型

那与不加泛型有区别吗?
比如直接List --------------------编程问答-------------------- 个人理解,不对的话,请高手指正!应该不一样:
List<?> 这个不管是什么,但是都是一样的。比如可以是List<Integer>,也可以是 List<String>,不管是什么,list里元素是一样的。
但List list里没有限制,同一个list里,list(0)可以是Iteger,而list(1)可以是String,即其每个元素可以不一样。 --------------------编程问答--------------------
引用 3 楼 nmyangym 的回复:
个人理解,不对的话,请高手指正!应该不一样:
List<?> 这个不管是什么,但是都是一样的。比如可以是List<Integer>,也可以是 List<String>,不管是什么,list里元素是一样的。
但List list里没有限制,同一个list里,list(0)可以是Iteger,而list(1)可以是String,即其每个元素可以不一样。

那又和List<E>有什么区别? --------------------编程问答-------------------- 加泛型只是编译器在编译期做检查而已。编译后都不会被擦除掉的。像这个List<?> list和不写是没有区别的。
不写的话就是在eclipse里会有黄线警告而已。 --------------------编程问答-------------------- 你可以比对下,加和没加的字节码,加了List<特定的类型>之后,Java编译出来的字节码有一个强制转型的 --------------------编程问答--------------------
引用 5 楼 suciver 的回复:
加泛型只是编译器在编译期做检查而已。编译后都不会被擦除掉的。像这个List<?> list和不写是没有区别的。
不写的话就是在eclipse里会有黄线警告而已。

多打了一个字不是编译后都不会被擦除掉的而是编译后都会被擦除 --------------------编程问答-------------------- 如果有 <?> 是不能往容器添加数据的。 --------------------编程问答--------------------
引用 5 楼 suciver 的回复:
加泛型只是编译器在编译期做检查而已。编译后都不会被擦除掉的。像这个List<?> list和不写是没有区别的。
不写的话就是在eclipse里会有黄线警告而已。

我想应该不会这么简单吧?如果没什么区别,加这个通配符就为了消除黄线?


引用 8 楼 zssazrael 的回复:
如果有 <?> 是不能往容器添加数据的。

你指的容器是哪个? --------------------编程问答--------------------
引用 4 楼 BaronZ 的回复:
引用 3 楼 nmyangym 的回复:个人理解,不对的话,请高手指正!应该不一样:
List<?> 这个不管是什么,但是都是一样的。比如可以是List<Integer>,也可以是 List<String>,不管是什么,list里元素是一样的。
但List list里没有限制,同一个list里,list(0)可以是Iteger,而list(1)可以是String,即其……

?没记错的话应该是wildcard,就是不定类型,楼上已经有解释了
E这种应该是所谓的带参的可以传递的,可以再次被引用,后面再用到E表示相同类型。 --------------------编程问答--------------------
引用 10 楼 dracularking 的回复:
引用 4 楼 BaronZ 的回复:
引用 3 楼 nmyangym 的回复:个人理解,不对的话,请高手指正!应该不一样:
List<?> 这个不管是什么,但是都是一样的。比如可以是List<Integer>,也可以是 List<String>,不管是什么,list里元素是一样的。
但List list里没有限制,同一个list里,list(0)可以是Iteger,而list(1)可以是St……

哪一楼解释了? --------------------编程问答-------------------- List<E>或List<?>如果只是单纯的定义的话是无区别的。但是如果你要引用的话就有区别了
List<E>表示说list中可以任意E类型的对象这个E可以被你在声明中引用到
例如在方法中
public <E> void a(List<E> list){
E e=list.get(0);
后面再对着e对象可以做各种操作
}
但是如果是?的
就不能在引用了
public void a(List<?> list){
? e=list.get(0);//这样就不行了。也就自然不能在用e这个引用了
} --------------------编程问答--------------------
引用 5 楼 suciver 的回复:
加泛型只是编译器在编译期做检查而已。编译后都不会被擦除掉的。像这个List<?> list和不写是没有区别的。
不写的话就是在eclipse里会有黄线警告而已。

刚刚试了一下,List<?>与List是有区别的,你也可以去上机试试


List<?> list = new ArrayList();
list.add("str");//出错,书中也提到了不能加入非null的值
list.add(null);//没错

但是不明白这个一般会用到哪些情景中 --------------------编程问答--------------------
引用 12 楼 suciver 的回复:
List<E>或List<?>如果只是单纯的定义的话是无区别的。但是如果你要引用的话就有区别了
List<E>表示说list中可以任意E类型的对象这个E可以被你在声明中引用到
例如在方法中
public <E> void a(List<E> list){
E e=list.get(0);
后面再对着e对象可以做各种操作
}
但是如果是?的
就不能在引用了
public void ……

这个有点明了。
就是说它返回的类型是不确定的?
而List<E>返回的类型是确定的。
应该是这么回事了
但是,像我上面说的,不知会在什么场景中使用到 --------------------编程问答--------------------
引用 12 楼 suciver 的回复:
public void a(List<?> list){
? e=list.get(0);//这样就不行了。也就自然不能在用e这个引用了

试了一下,是能拿到的。
但是就像List一样,要强转类型。
比如String s = (String) list.get(0);
现在我能想到的应用场景就是,只给读(get),不给写(add)。
--------------------编程问答-------------------- 那你就定义List<String> list;这样获取出来的就已经是String了。如果在代码中list.add(1);这样的写法都是编译不过的。但是由于泛型只在编译期有效,如果别人用反射方式来add的话那一样能add其它类型的数据 --------------------编程问答--------------------
引用 16 楼 suciver 的回复:
那你就定义List<String> list;这样获取出来的就已经是String了。如果在代码中list.add(1);这样的写法都是编译不过的。但是由于泛型只在编译期有效,如果别人用反射方式来add的话那一样能add其它类型的数据

我明白你一直说的那个运行期泛型擦除,java的语法糖。
我现在问的不是List<E>
而是问List<?> --------------------编程问答--------------------
import java.util.LinkedList;
import java.util.List;

public class Hello {
    public static void main(String[] args) {
        List<?> listx = new LinkedList<?>(); // Error
        List<?> listy = new LinkedList<Integer>(); // Ok

        List<Integer> list1 = new LinkedList<Integer>();
        list1.add(1);
        list1.add(2);
        list1.add(3);

        List<String> list2 = new LinkedList<String>();
        list2.add("A");
        list2.add("B");
        list2.add("C");

        List list3 = new LinkedList();
        list3.add(1);
        list3.add("A");
        list3.add(Hello.class);

        display(list1);
        display(list2);
        display(list3);
    }

    public static void display(List<?> list) {
        // list.add(1); // Error
        System.out.println(list);
    }
}
--------------------编程问答--------------------
引用 11 楼 BaronZ 的回复:
引用 10 楼 dracularking 的回复:引用 4 楼 BaronZ 的回复:
引用 3 楼 nmyangym 的回复:个人理解,不对的话,请高手指正!应该不一样:
List<?> 这个不管是什么,但是都是一样的。比如可以是List<Integer>,也可以是 List<String>,不管是什么,list里元素是一样的。
但List list里没有限制,同……

1楼和3楼都有提到这个? --------------------编程问答--------------------
引用 15 楼 BaronZ 的回复:
引用 12 楼 suciver 的回复:public void a(List<?> list){
? e=list.get(0);//这样就不行了。也就自然不能在用e这个引用了

试了一下,是能拿到的。
但是就像List一样,要强转类型。
比如String s = (String) list.get(0);
现在我能想到的应用场景就是,只给读(get),不……

直接写貌似是不行,可能是因为?未必是String或String祖先类

但这样可以

List<String> list1 = new ArrayList<String>();
list1.add("str");
List<?> list2 = list1;

也能看出点什么了 --------------------编程问答-------------------- 通配符?不会发生斜边 List<?>不知道是什么类型  所以就不能进行put操作 --------------------编程问答-------------------- 测试了一下18楼Inhibitory的代码,我在3楼说的就是错误的!
就是List<?> 可以代表一个非泛型的List.(18楼代码的26行).
那似乎是List<?>和List的区别是,前者限制写入,而后者允许写入,其他一样!麻烦 Inhibitory解释一下!谢谢! --------------------编程问答--------------------
引用 20 楼 dracularking 的回复:
直接写貌似是不行,可能是因为?未必是String或String祖先类

你上机试一下,直接写是可以的。你用List时,get回来也未必是String,所以有可能运行出错而已


引用 22 楼 nmyangym 的回复:
测试了一下18楼Inhibitory的代码,我在3楼说的就是错误的!
就是List<?> 可以代表一个非泛型的List.(18楼代码的26行).
那似乎是List<?>和List的区别是,前者限制写入,而后者允许写入,其他一样!麻烦 Inhibitory解释一下!谢谢!

你没有引用他,我帮你一下


引用 18 楼 Inhibitory 的回复:
……

Inhibitory,麻烦能否解释下22楼的疑问? --------------------编程问答--------------------
引用 18 楼 Inhibitory 的回复:
Java code?123456789101112131415161718192021222324252627282930313233import java.util.LinkedList;import java.util.List; public class Hello {    public static void main(String[] args) {     ……

我把您的代码,修改一下,把下面的语句:

public static void display(List<?> list) {

改成:

public static void display(List list) {//把List<?>换成List

结果一样的。是否说明List类型可以代表任何泛型List呢?
但此时可以把注视掉的语句去掉注释,每个list都能加进去一个Integer 了,这样的话,list2显然被破坏了。所以List<?>不允许写入,这样保护了泛型的的规则。
可能问的不是很清楚,关键就是:
List<?> 和List除了前者限制写入外,没有其他区别?
--------------------编程问答--------------------
引用 24 楼 nmyangym 的回复:
引用 18 楼 Inhibitory 的回复:
Java code?123456789101112131415161718192021222324252627282930313233import java.util.LinkedList;import java.util.List; public class Hello {    public static void main(String[] ……

官方给出了下面的例子,我也插了代码进去。
我想官方代码的意思就List<?>相当于List<? extends Object>
而List<Object>已经定了只能传入Object.
但是,List<?>又和List<E>有什么区别呢?写入的区别?

//Consider the following method, printList:

public static void printList(List<Object> list) {
    for (Object elem : list)
        System.out.println(elem + " ");
    System.out.println();
}
//The goal of printList is to print a list of any type, but it fails to achieve that goal — it prints only a list of Object instances; it cannot print List<Integer>, List<String>, List<Double>, and so on, because they are not subtypes of List<Object>. To write a generic printList method, use List<?>:

public static void printList(List<?> list) {
    for (Object elem: list)
        System.out.print(elem + " ");
    System.out.println();
}
//Because for any concrete type A, List<A> is a subtype of List<?>, you can use printList to print a list of any type:

List<Integer> li = Arrays.asList(1, 2, 3);
List<String>  ls = Arrays.asList("one", "two", "three");
printList(li);
printList(ls);//这样的话,如果调用List<Object>那个方法确实不行,但是为什么不用List<E>呢?
//----------------我的代码
public static <E> void printList(List<E> list) {
    for (Object elem: list)
        System.out.print(elem + " ");
    System.out.println();
}
//这样的话,上面的结果也是可以打的



--------------------编程问答-------------------- 怎么直接写可以?我说的直接写是指这个

List<?> list = new ArrayList();
list.add("str");//出错,书中也提到了不能加入非null的值 --------------------编程问答-------------------- 25楼例子不足以说明其区别

? 就代表任意类型,unknown type
E 是 formal type parameter 确定类型只是未知

就是这样的区别我觉得 --------------------编程问答-------------------- 泛型是jdk1.5加的,本着向下兼容的原则,不指定泛型时类型与老版本相同,就是Object。
<?>意思是未知类型,就是不设上下限
<? extend Object>意思是继承于Object的未知类型
<? super Object>意思是Object的祖先类型
后面还可以用&接口名序列指定接口
<E>可以应用以上的格式,对于类,可以在类声明时指定泛型类型,对于方法可以通过参数传入<E>来确定运行时泛型类型 --------------------编程问答-------------------- 讨论不如直接查书

<Effective Java> 第二版 P109~146,多读两遍。 --------------------编程问答--------------------
引用 29 楼 raistlic 的回复:
讨论不如直接查书

<Effective Java> 第二版 P109~146,多读两遍。


这个建议适用于任何发Java泛型疑问贴的同学,在论坛里见了不少对泛型有疑问的帖子,没有一个问题超出书里描述的范围。 --------------------编程问答-------------------- ? 通配符。也可以理解为占位符。
泛型的限定;
? extends E: 可以接收E类型或者E的子类型。上限。
? super E: 可以接收E类型或者E的父类型。下限



public static void printColl(Collection<? extends Person> al)
    {
        Iterator<? extends Person> it = al.iterator();
 
 
        while(it.hasNext())
        {
            System.out.println(it.next().getName());
        }
    }
 
class Person
{
    private String name;
    Person(String name)
    {
        this.name = name;
    }
    public String getName()
    {
        return name;
    }
}
 
class Student extends Person
{
    Student(String name)
    {
        super(name);
    }
 
}
--------------------编程问答-------------------- 除 --------------------编程问答--------------------
引用 27 楼 dracularking 的回复:
25楼例子不足以说明其区别

? 就代表任意类型,unknown type
E 是 formal type parameter 确定类型只是未知

就是这样的区别我觉得


引用 28 楼 xodbc 的回复:
泛型是jdk1.5加的,本着向下兼容的原则,不指定泛型时类型与老版本相同,就是Object。
<?>意思是未知类型,就是不设上下限
<? extend Object>意思是继承于Object的未知类型
<? super Object>意思是Object的祖先类型
后面还可以用&接口名序列指定接口
<E>可以应用以上的格式,对于类,可以在类声明时指定泛型类型,对于方法可以通过参数传……


引用 30 楼 raistlic 的回复:
引用 29 楼 raistlic 的回复:
讨论不如直接查书

<Effective Java> 第二版 P109~146,多读两遍。

这个建议适用于任何发Java泛型疑问贴的同学,在论坛里见了不少对泛型有疑问的帖子,没有一个问题超出书里描述的范围。

那你们觉得下面给出的两个方法有区别吗?

public static String classify(List<?> lst){
return "List";
}
public static String classify(List<E> lst){
return "List";
}

--------------------编程问答-------------------- 或者能不能通俗地说一下上面两个例子的区别? --------------------编程问答--------------------
引用 34 楼 BaronZ 的回复:
或者能不能通俗地说一下上面两个例子的区别?


第二个应该编译不了吧


// 应该加上泛型声明
public static <E> String classify(List<E> lst){
        return "List";
}


区别很细微……
首先第一个 List<?> 是只读的,如果你试图在方法内调用 lst.add(),只要不是 add(null) 都不能通过编译;
第二个可以用强转的方式add(),虽然可能会破坏类型安全。

然后第二个方法是泛型方法,你在调用的时候可以指定具体类型,这个具体类型可以作为返回类型使用:


public static <E> E classify(List<E> lst){
        return "List";
}

// user code:
String s = ClassName.<String>classify(Arrays.asList("1","2","3"));
Integer i = ClassName.<Integer>classify(Arrays.asList(1, 2, 3));
--------------------编程问答--------------------
引用 35 楼 raistlic 的回复:
引用 34 楼 BaronZ 的回复:或者能不能通俗地说一下上面两个例子的区别?

public static <E> E classify(List<E> lst){
        return "List";
}
 
// user code:
String s = ClassName.<String>classify(Arrays.asList("1","2","3"));
Integer i = ClassName.<Integer>classify(Arrays.asList(1, 2, 3));


例子中的 return 语句错了,不过意思你能明白就行了。

--------------------编程问答--------------------
引用 34 楼 BaronZ 的回复:
或者能不能通俗地说一下上面两个例子的区别?

?和String一样是个类型常量,无法改变,必须和完全相同的类型才能匹配,E是一个类型变量,运行时给这个变量赋值类型后他就指代那种类型。
--------------------编程问答-------------------- 除 --------------------编程问答--------------------
引用 35 楼 raistlic 的回复:
1.第二个应该编译不了吧
2.区别很细微……
首先第一个 List<?> 是只读的,如果你试图在方法内调用 lst.add(),只要不是 add(null) 都不能通过编译;
第二个可以用强转的方式add(),虽然可能会破坏类型安全。

然后第二个方法是泛型方法,你在调用的时候可以指定具体类型,这个具体类型可以作为返回类型使用:

1.是的,我大概写了个例子,没太严谨,要在类或者方法那里先声明。
2.看明白你意思了。即用?不可以做为具体类型返回?但是感觉有点奇怪。这么做的用途是什么?
即声明List<?>,让你来用的话,你会用在什么场景?


引用 37 楼 xodbc 的回复:
引用 34 楼 BaronZ 的回复:
或者能不能通俗地说一下上面两个例子的区别?
?和String一样是个类型常量,无法改变,必须和完全相同的类型才能匹配,E是一个类型变量,运行时给这个变量赋值类型后他就指代那种类型。

没太看明白 --------------------编程问答--------------------
引用 39 楼 BaronZ 的回复:
引用 35 楼 raistlic 的回复:1.第二个应该编译不了吧
2.区别很细微……
首先第一个 List<?> 是只读的,如果你试图在方法内调用 lst.add(),只要不是 add(null) 都不能通过编译;
第二个可以用强转的方式add(),虽然可能会破坏类型安全。

然后第二个方法是泛型方法,你在调用的时候可以指定具体类型,这个具体类型可以作为返回类……

我只是打个比方,范型是对元素的类型限制,如果你在编译前没有确定类型,你就应该用<E>通过接收传入类型来确定需要限定的类型,如传入String,他就兼容String,是动态的类型限定;
如果你在编译前能确定类型就写<Type>,这样必须完全兼容Type类型才能传入,也就是静态的类型限定,<?>就是指定一个未知的Type,因为必须完全兼容才能传入,而未知类型与任何类型都不能完全兼容,所以只能匹配null值。 --------------------编程问答-------------------- 不明白List<?>只能加null,什么场景会用到? --------------------编程问答--------------------
引用 12 楼 suciver 的回复:
List<E>或List<?>如果只是单纯的定义的话是无区别的。但是如果你要引用的话就有区别了
List<E>表示说list中可以任意E类型的对象这个E可以被你在声明中引用到
例如在方法中
public <E> void a(List<E> list){
E e=list.get(0);
后面再对着e对象可以做各种操作
}
但是如果是?的
就不能在引用了
……
这个说的有道理,加泛型是为了约束放入里面的值,也方便引用,不过还真没用过这个List <?>,顶多,List<Object>. --------------------编程问答-------------------- 可以用在这种场合

void printCollection(Collection<?> c) {
    for (Object e : c) {
        System.out.println(e);
    }
}


但又可以被E代替,貌似唯一性职能没有体现出来 --------------------编程问答--------------------
引用 28 楼 xodbc 的回复:
泛型是jdk1.5加的,本着向下兼容的原则,不指定泛型时类型与老版本相同,就是Object。
<?>意思是未知类型,就是不设上下限
<? extend Object>意思是继承于Object的未知类型
<? super Object>意思是Object的祖先类型
后面还可以用&接口名序列指定接口
<E>可以应用以上的格式,对于类,可以在类声明时指定泛型类……


有这种用法????!! --------------------编程问答-------------------- "那又和List<E>有什么区别?"
我最近在学习这个泛型,有点体会,不知对否,不对的话,请指正!
1    ?这个通配符代表已知类型,他与 ? extends someClass, someClass是一样的,出现于使用某个已
     经定义的泛型类,泛型方法或泛型接口中。
2    E 及E extends someClass 出现于定义泛型类,泛型接口,泛型方法中。
3    List<?> 和 List<E>是有本质的区别的。List<?>, 是在使用泛型类List,这个不管具体里面是什么,
     但已经是一个明确的,可被赋值的类型了。可以给它赋值。
     比如,List<?> listUnknown = null;
           listUnknown = new ArrayList<Integer>();
         或listUnknown = new LinkedList<String>();
         等等。
     但List<E> 它还不是一个已知的类,还不能被赋值。只能出现在定义泛型的方法中。(List已经是
     被定义成泛型类了).如33楼,后面的代码,需加上<E> 类型声明。
--------------------编程问答--------------------
引用 45 楼 nmyangym 的回复:
"那又和List<E>有什么区别?"
我最近在学习这个泛型,有点体会,不知对否,不对的话,请指正!
1    ?这个通配符代表已知类型,他与 ? extends someClass, someClass是一样的,出现于使用某个已
     经定义的泛型类,泛型方法或泛型接口中。
2    E 及E extends someClass 出现于定义泛型类,泛型接口,泛……

33楼的35楼有补充

public static <E> String classify(List<E> lst) {
    return "List";
} --------------------编程问答-------------------- 说错了,不过?代表未知类型


List<E> listUnknown1 = new ArrayList<Integer>();

这里的E代表的恰恰是一个确定但未知的类型,所以编译不通过(因为E未必是Integer)
补充:Java ,  Java SE
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,