Why does an instance of Test<?> accept non-null objects in the constructor?

Gio*_*gos 0 java generics wildcard

The wildcard ? in generics represents an unknown type and accepts only null. However, in the following example, the constructor accepts (for example) a String object, even though I have declared an instance of Test<?>.

public class Test<T> {
    private T object;
    public Test(T object) {
        this.object = object;
    }
    public void set(T object) {
        this.object = object;
    }
    public static void main(String[] args) {
        Test<?> test = new Test<>("Test");    // compiles fine
        //test.set("Test");    // compiler error
    }
}
Run Code Online (Sandbox Code Playgroud)

Why does this compile normally?

Joh*_*ger 5

the constructor accepts (for example) a String object, even though I have declared an instance of Test<?>.

No. You have declared a variable of type Test<?>. You have created an instance of type Test<X>, for some X that the compiler is obliged to figure out for itself such that the overall statement is valid. That is, here:

        Test<?> test = new Test<>("Test");    // compiles fine
Run Code Online (Sandbox Code Playgroud)

... the <> does not mean "copy the type parameter from the type of the variable" or anything much like that. Test<?> is not the type of the expression new Test<>("Test"). That the chosen type parameter must make the type assignable to type Test<?> is one of the constraints that must be satisfied, but that's trivial, because any X will do for that.

Formally, Java will infer the most specific type parameter that satisfies all the constraints for the statement to be valid. new Test<X>("Test") is valid only for a relatively few Xs:

  • java.lang.Object
  • java.lang.Serializable
  • java.lang.Comparable<String> and a few other variations on Comparable
  • java.lang.CharSequence
  • java.lang.constant.Constable (since Java 12)
  • java.lang.constant.ConstantDesc (since Java 12)
  • java.lang.String
  • intersection types of combinations of the above

String is the most specific of those, so it is the one chosen.

然而,实际上,Java 实际上根本不需要选择特定的类型参数。它只需让自己满意,至少有一个有效。


表达式test.set("Test")是不同的情况。test分配给的对象被推断为具有类型并不重要Test<String>在这里,与在所有情况下一样,Java 根据所涉及的任何变量的声明类型来分析表达式。 test是用 type 声明的Test<?>。因此,Java 没有任何类型可以确信test.set()会接受作为参数。但是,该值null与每个引用类型兼容,因此无论对象test引用的特定类型参数是什么,null都是可接受的参数test.set()