Java 泛型、类型参数和通配符

Java 泛型、类型参数和通配符,java,generics,types,wildcard,Java,Generics,Types,Wildcard,我试图理解java泛型,它们似乎非常难以理解。例如,这很好 public class Main { public static void main(String[] args) { List<?> list = null; method(list); } public static <T> void method(List<T> list) { } } 公共类主{ 公共静态void main(字符串

我试图理解java泛型,它们似乎非常难以理解。例如,这很好

public class Main {

    public static void main(String[] args) {
        List<?> list = null;
        method(list);
    }

    public static <T> void method(List<T> list) { }
}
公共类主{
公共静态void main(字符串[]args){
List=null;
方法(列表);
}
公共静态无效方法(列表){}
}
。。。就这样

public class Main {

    public static void main(String[] args) {
        List<List<?>> list = null;
        method(list);
    }

    public static <T> void method(List<T> list) { }
}
公共类主{
公共静态void main(字符串[]args){
列表>>列表=空;
方法(列表);
}
公共静态无效方法(列表){}
}
。。。但这并没有编译:

public class Main {

    public static void main(String[] args) {
        List<List<?>> list = null;
        method(list);
    }

    public static <T> void method(List<List<T>> list) { }
}
final List<String> strings = ...
final List<Object> objects = strings;
公共类主{
公共静态void main(字符串[]args){

List泛型类型要理解的主要问题是它们不是协变的

因此,尽管您可以这样做:

final String string = "string";
final Object object = string;
以下内容将不会编译:

public class Main {

    public static void main(String[] args) {
        List<List<?>> list = null;
        method(list);
    }

    public static <T> void method(List<List<T>> list) { }
}
final List<String> strings = ...
final List<Object> objects = strings;

因此,由于协方差性,不可能将
列表
安全地分配给任何其他泛型
列表

这几乎让我信服。唯一的诡辩是在示例1中,当你说T可以分配给对象类型时。这对我来说似乎不正确-如果你用一个非泛型的方法替换该方法,则不起作用t、 当然,在示例1中,编译器能够看到类型t与列表匹配。为什么编译器在示例4中看不到相同的东西?这里也是一样-听起来很有说服力,但…不完全:当显式传递
列表时,可以调用最后一个方法,因此参数不应该指向缺少的cov变量,而是类型推断系统的一个弱点,以及其中涉及的通配符……你不是指
objects.add(1)
?@pbabcdefp添加了一个澄清的编辑-希望能更清楚地解释这个问题。一些被阻止的事情没有出现您提到的异常的风险。例如,如果我有一个带有签名的方法
静态列表m1(列表a)
和另一个带有签名的方法
静态无效m2(列表a)
,您会发现如果
x
属于
List
类型,则
m2(m1(x))
不会编译!返回
List
的方法返回的值不会被接受为具有
List
类型参数的方法的参数。但是
m2(m1(x))
是无风险的-您可以使用参数
List
编写一个方法,其主体是
m2(m1(x))
并且它接受了一个
列表
很好。只是好奇,Boris的回答是否足够,或者你对此是否仍有疑问?@Radiodef我现在对此一点也不怀疑。花了一段时间,但我终于明白了。自从我写了这个问题以来,我一直在回答大量的泛型问题!好的,没问题。;)我看到了评论,并思考了wr这是一个额外的问题。通常不会出现这种情况。只有第一个示例中的通配符可以被捕获。@Radiodef对我来说,关键是当我最终了解到
列表
是一种类型时(而
不是)因此,
列表
,而
列表
意味着某种未知类型的
列表
。是的,这里错误的本质是:如果您有一个
列表
可以像第一个示例中那样被视为类型(捕获)。但是如果您有一些嵌套类型,如
映射
,则只有“最外层”中的通配符可以捕获类型(因此
映射
而不是
列表
)。Boris的回答说明了为什么(我们可以做不安全的事情),但没有说明如何捕获的细节。通配符的语义略有不同,这取决于它是“外部”类型还是“内部”类型。
final List<List<Integer>> list = ...
final List<List<?>> wildcard = list;
wildcard.add(Arrays.asList("oops"));