What type of <?> When creating list instances?
I saw in several different places people who instantiate a list or ArrayList, for example:
List<?> l = new ArrayList<>(); Which type? Does this mean that there can be any types in it? If so, why should this be used instead of just and ArrayList?
Does this mean that it can contain any types?
Not. This means that your variable l can refer to a list parameterized by any type. Thus, this is actually a limitation: you will not be allowed to add any object to l because you do not know what elements it takes. To give a concrete example, l can be a List<String> , or it can be a List<ExecutorService> .
As Marko pointed out correctly, its unknown restriction on the type of List.
Java docs state that:
An unlimited wildcard type is specified using a wildcard character (?), For example,
List<?>. This is called an unknown type list. There are two scenarios in which an unlimited template is a useful approach:
- If you are writing a method that can be implemented using the functions provided in the Object class.
- When code uses methods in a general class that are independent of the type parameter. For example, List.size or List.clear. In fact,
Class<?>used so often because most methods in the Class are independent of T.
Let me tell you about this for a long time; read it to fall asleep :)
Let's start from this point. To invoke a generic method, type arguments must be provided. (If the method is not called in a raw way, that is, in an erasable form, which is another topic :)
For example, to call Collections.<T>emptyList() you must specify T It can be explicitly set by the programmer -
List<String> list = Collections.<String>emptyList(); // T=String But it is tiring and ridiculous. Obviously, in this context T can only be String . It is stupid if the programmer has to repeat the obvious.
What is where type inference is useful. We can omit the type argument, and the compiler can conclude what the programmer intends to do
List<String> list = Collections.emptyList(); // T=String is implied Remember that <String> is still provided implicitly by the programmer.
Presumably, the programmer is the omniscient dictator of all type arguments, and the compiler and programmer have a common understanding of when type arguments can be omitted and taken out of context. When the programmer omits the type argument, he knows that the compiler can output it exactly as he expected, based on a strict algorithm (which he masters :) This is not the discretion of the compiler to select and select type arguments, rather, the programmer does this and passes it to the compiler.
Actually, the type inference is so complicated, few no programmer knows what happens in many cases :) The programmer is more like a dictator making undefined commands, and the compiler tries to best understand this. We mostly write code by intuition, ignoring the details, and we believe that the code does what we want, if the compiler approves it.
In any case, all type arguments are captured accurately and predictably at compile time. Any argument of a skipped type is equivalent to an explicit one.
Some type arguments are "incontrovertible," for example. type variable introduced by capture conversion. They cannot be explicitly specified, they can only be displayed. (However, the programmer must know what they are, even if they cannot be named)
In the previous example, T can only be displayed as String , there are no other options. But in many cases there are more candidates for T , and the type inference algorithm must have a strategy for solving one of the candidates. For example, consider this lonely statement
Collections.emptyList(); T can be any type; T allowed by Object , because, well, there is no good reason for solving it with anything else, such as Integer or String , etc. Object is more special because it is a supertype of all.
Now let's move on to the designers. Formally speaking, constructors are not methods. But they are very similar in many aspects. In particular, the type inference for constructors is almost the same as for methods. A call to the constructor of the CLASS class has the form new CLASS(args) .
Like methods, a constructor can be shared, with its own type parameters. For example,
class Bar { <T>Bar(T x){ .. } and type inference also works with generic constructors.
new Bar("abc"); // inferred: T=String To explicitly specify type arguments for a constructor,
new <String>Bar("abc"); This is quite rare, although the constructor is common.
The general constructor is different from the general CLASS! Consider this
class Foo<T> { Foo(T x){ .. } The class is general, the constructor is not. To call the constructor for the class Foo<String> , do
new Foo<String>(""); // CLASS = Foo<String> A call of the type of the method that we talked about so far is not applicable here, because the constructor is not even general. In Java 5/6, there is no type inference on CLASS, so <String> must be explicitly specified. This is stupid because <String> obvious in this context. There were workarounds (i.e., using static factory methods), but people, of course, were very upset and demanded a solution.
In Java 7, this problem is solved through the "diamond output" -
new Foo<>(""); // inferred: T=String "diamond" refers to a curious operator <> . Required; we cannot just write
new Foo(""); because it already had a different meaning - the "raw" Foo constructor is called.
With diamond output, we can do what we cannot in Java 5/6
List<Object> list = new ArrayList<>(); // Java 7. inferred: E=Object // equivalent to List<Object> list = new ArrayList<Object>(); // <Object> is required in Java 5/6 Remember that T=Object is still provided using diamond output.
Finally, we will return to your original question
List<?> list = new ArrayList<>(); Here E=Object deduced (what else?). Code equivalent
List<?> list = new ArrayList<Object>(); Yep, the list object is really an ArrayList<Object> , not an ArrayList<SomethingElse> .
Also note that the following would be illegal and meaningless
List<?> list = new ArrayList<?>(); ^^^ CLASS in new CLASS(args) must be a specific type. We can instantiate an ArrayList specific element type.
The declared type List<?> list variable list is too general. For a local variable, IMO's best practice is to declare it a more specific type.
ArrayList<Object> list = new ArrayList<>(); Do not use <?> Here - it just causes confusion for everyone.
In the corresponding note, many people will argue about the "program against the interface"
List<Object> list = new ArrayList<>(); ^^^^ This is wrong IMO. To whom do we provide abstraction in the local block? Use the most specific type of implementation for maximum clarity; use abstract types in interfaces.
zzzzzzzzzz