Java 泛型类型和通配符类型之间的差异

Java 泛型类型和通配符类型之间的差异,java,generics,wildcard,Java,Generics,Wildcard,我是泛型方面的新手,我的问题是:两个函数之间有什么区别: 职能1: public static <E> void funct1 (List<E> list1) { } 公共静态无效函数1(列表1){ } 职能2: public static void funct2(List<?> list) { } publicstaticvoidfunct2(列表){ } 列表作为参数类型表示参数必须是具有任何对象类型的项目列表。此外,还可以绑定E参数来声明对函

我是泛型方面的新手,我的问题是:两个函数之间有什么区别:

职能1:

public static <E> void funct1  (List<E> list1) {

}
公共静态无效函数1(列表1){
}
职能2:

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

}
publicstaticvoidfunct2(列表){
}

列表作为参数类型表示参数必须是具有任何对象类型的项目列表。此外,还可以绑定
E
参数来声明对函数体中列表项的引用


作为参数类型的列表具有相同的语义,只是除了使用
对象
之外,无法声明对列表中项目的引用。其他帖子提供了额外的细微差别。

泛型使集合更加类型安全

列表
:E这里是类型参数,可用于确定列表的内容类型,但在
运行时
没有检查内容的方法

Generics are checked only during compilation time.

第一个签名是:list1是一个Es列表

第二个签名是:list是某种类型的实例列表,但我们不知道其类型

当我们尝试更改方法时,差异变得明显,因此它需要第二个参数,该参数应添加到方法内的列表中:

import java.util.List;

public class Experiment {
    public static <E> void funct1(final List<E> list1, final E something) {
        list1.add(something);
    }

    public static void funct2(final List<?> list, final Object something) {
        list.add(something); // does not compile
    }
}
import java.util.List;
公共课堂实验{
公共静态无效函数1(最终列表列表1,最终E){
增加(某物);
}
公共静态无效funct2(最终列表、最终对象){
list.add(某物);//不编译
}
}
第一个很好用。您不能将第二个参数更改为任何实际可编译的参数

事实上,我刚刚发现了一个更好的例子来证明这种差异:

public class Experiment {
    public static <E> void funct1(final List<E> list) {
        list.add(list.get(0));
    }

    public static void funct2(final List<?> list) {
        list.add(list.get(0)); // !!!!!!!!!!!!!! won't compile !!!!!!!!!
    }
}
公共课堂实验{
公共静态无效函数1(最终列表){
list.add(list.get(0));
}
公共静态无效函数2(最终列表){
list.add(list.get(0));//!!!!!!!!!!!!!!!!!!!!!不会编译!!!!!!!!!
}
}
有人可能会问为什么我们需要
,而它只限制了我们可以用它做什么(正如@Babu\u Reddy\u H在评论中所做的那样)。我看到了通配符版本的以下好处:


  • 打电话的人不必太了解他所传递的对象。例如,如果我有一个列表映射:
    Map第一个是一个函数,它接受的参数必须是E类型的项目列表

    第二个示例类型未定义

    List<?> list
    
    列表
    

    所以您可以传递任何类型对象的列表

    (自编辑后)这两个函数签名对外部代码具有相同的效果——它们都将任何
    列表
    作为参数。通配符相当于只使用一次的类型参数。

    我通常通过与逻辑量化(即通用量化和存在量化)的比较来解释和之间的区别

    • 对应于“forall E,…”
    • 对应于“存在某物(由表示)以至于……”
    因此,下面的泛型方法声明意味着,对于所有类类型E,我们定义
    funct1

    public static <E> void funct1  (List<E>; list1) {
    
    }
    

    除了前面提到的这些区别之外,还有一个额外的区别:您可以显式设置泛型方法调用的类型参数:

    List<Apple> apples = ...
    ClassName.<Banana>funct2(apples); // for some reason the compiler seems to be ok
                                   // with type parameters, even though the method has none
    
    ClassName.<Banana>funct1(apples); // compiler error: incompatible types: List<Apple>
                                      //                 cannot be converted to List<Banana>
    
    列出苹果=。。。
    ClassName.funct2(苹果);//出于某种原因,编译器似乎是正常的
    //使用类型参数,即使该方法没有
    ClassName.funct1(苹果);//编译器错误:不兼容的类型:列表
    //无法转换为列表
    

    ClassName
    是包含方法的类的名称。)

    如果String指的是
    java.lang.String
    ,我认为funct2在通配符表达式中是多余的,因为
    java.lang.String
    是最终类,无法扩展。这很奇怪,因为从技术上讲,您不能扩展字符串,因为它是最终类。但是,对使用函数2没有限制:因为它允许字符串的子类(尽管不可能)或字符串类本身……这里给出了最佳答案。很抱歉,我的问题有点错误,我编辑了这个问题。@Gene您能解释一下为什么第二个函数参数必须是从字符串派生的对象列表吗。由于参数没有定义其对象类型“List List”,所以我认为它可以保存任何类型的对象以及字符串。不要说“first”或“second”,而是包含相关代码以避免混淆(以及对OP的编辑)。我只能使用吗?如上面的示例列表所示?那么什么类型的对象可以存储“”,用于使集合能够从被调用的方法中获取参数,该方法扩展了这里提到的类型“此示例中的列表用户尚未定义类型。。。那么它将采用什么类型的对象呢?List表示一个具有未知对象类型的列表。值得注意的是,它不仅仅是一个on puts。使用
    List
    ,您可以编写
    E obj=List.get(0)
    ,但使用
    List
    您只能编写
    Object obj=List.get(0)
    @Jens schauder。二者都E只允许对对象调用对象方法。一个人可以添加到列表中,但不能对列表执行相同的操作。那为什么我们需要这份清单呢?
    public static void funct2(List<?> list) {
    
    }
    
    List<Apple> apples = ...
    ClassName.<Banana>funct2(apples); // for some reason the compiler seems to be ok
                                   // with type parameters, even though the method has none
    
    ClassName.<Banana>funct1(apples); // compiler error: incompatible types: List<Apple>
                                      //                 cannot be converted to List<Banana>