Java的类型参数通配符到底意味着什么?Foo和Foo <?>之间的真正区别是什么?

mws*_*ltn 8 java generics types java-ee

对于通用接口:

public interface Foo<T> {
    void f(T t); 
} 
Run Code Online (Sandbox Code Playgroud)

两个字段之间的区别:

public class Bar {
    Foo foo1; 
    Foo<?> foo2; 
}
Run Code Online (Sandbox Code Playgroud)

foo2是通用类型而foo不是.因为?是一个通配符(我认为这意味着任何类型),并且每个类型都是Object的子类型,所以我期望Foo<?>Foo<Object>在语义和语法上等同.

但是,请查看以下内容:

public class Puzzler {
    void f() {
        Integer i = null; 
        Foo<?> foo1 = null;
        foo1.foo(i); // ERROR 
        Foo foo2 = null; 
        foo2.foo(i); // OKAY
        Foo<Integer> foo3 = null; 
        foo3.foo(i); // OKAY 
        Foo<Object> foo4 = null; 
        foo4.foo(i); // OKAY
    }

    private interface Foo<T> {
        void foo(T t);
    } 
}
Run Code Online (Sandbox Code Playgroud)

所以,Foo<?>Foo<Object>不是同一个语法.

这里发生了什么?我很想解决这个问题.

Ted*_*opp 6

Foo<?>在语义上是相同的Foo<? extends Object>:它是一个Foo具有特定类型的类型参数,但唯一知道的"某事"是它的一些子类Object(这并不是说太多,因为所有类都是子类Object).Foo<Object>另一方面,Foo具体是带有类型参数的Object.虽然一切都是分配兼容Object,而不是一切都将分配兼容?,其中?延伸Object.

以下是为什么Foo<?>应该生成错误的示例:

public class StringFoo implements Foo<String> {
    void foo(String t) { . . . }
}
Run Code Online (Sandbox Code Playgroud)

现在将您的示例更改为:

Foo<?> foo1 = new StringFoo();
Run Code Online (Sandbox Code Playgroud)

既然iInteger,那么编译器就没有办法允许foo1.foo(i)编译.

注意

Foo<Object> foo4 = new StringFoo();
Run Code Online (Sandbox Code Playgroud)

也不会根据该规则编译匹配参数化类型,因为ObjectString都是可证明不同的类型.

Foo(通常没有类型参数 - 原始类型)通常应被视为编程错误.但是,根据Java语言规范(第4.8节),编译器接受此类代码以便不破坏非泛型的遗留代码.

由于类型擦除,这些都不会产生字节代码.也就是说,它们之间的唯一区别是在编译时.