18
Understanding the syntax
Considering the important part:
<T> Optional<T> empty()
T
is a type variable. This works more or less like a variable in a template, where you can replace T
by some guy, remembering that guy in Java is synonymous with class (not to be confused with *primitive types).
The first <T>
says: this method will use a generic type T
somewhere. It’s like a hint to the compiler (and to the inattentive programmer).
The type of return of the method Optional<T>
then says the generic type of Optional
must be of the same generic type as the.
Confusing, right? When will Java know the type T? This occurs at the moment when you will use the method. As @Maniero has already said, when you call the method by assigning the result to a certain type, Java can infer the type used in that call.
For example, if you are assigning the return of the method to a variable of type Optional<String>
, then T = String
and you can read the method as:
Optional<String> empty()
In fact, the more advanced Ides like Eclipse and Intellij will show the signature with the proper replacement of T
during autocomplete whenever it is possible to infer type by the current context.
It is worth remembering that the inference of generic types is a compilation-time guarantee that there will be no incompatibility between types during assignments and accesses. Therefore, this has no influence during the execution of the program.
Another example
This is not the only way for the compiler to subunderstand the generic type. A classic example is a method that returns the same type passed by parameter:
static <T> T instanciar(Class<T> classe) throws IllegalAccessException, InstantiationException {
return classe.newInstance();
}
As in the first example, <T>
says the method is generic, the return will be of the type T
and the method receives a class of type Class<T>
. Example of use:
Cliente c = instanciar(Cliente.class);
Produto p = instanciar(Produto.class);
How Java Infers the Type T
here? The compiler looks at the type of class passed in the parameter and thus ensures that the returned value is of the same type.
Different from the case of the question, where the type is inferred by the variable that receives the return of the method, here the type is inferred by one of the parameters.
For example, Cliente.class
is an attribute that returns an object of type Class<Cliente>
, so the return of the first call will be to type Cliente
, where T = Cliente
.
Because <T>
is required prior to the type of the method
There are generic classes/interfaces and generic methods. If the class/interface is generic, its methods can use the generic type.
Example:
interface Generico<T> {
T empty();
}
Or:
class Generico<T> {
T empty() { ... }
}
However, if only the method is generic, the declaration needs to come before the method. This holds true for static and instance methods.
Note that you can mix generic classes and methods and use different names for type variables:
class Generico<T> {
T empty() { ... }
static <X> Optional<X> empty() { ... }
<Y> Optional<Y> of(Y instance) { ... }
}
However, consider that there is a convention for type variable names.
I believe that the decision to require the generic type declaration was taken for reasons of readability of the code, as well as avoiding obscure syntaxes so as not to confuse a generic type T
with a class T {}
.
Generic type is strictly necessary?
No. Java generics are just a security issue for the programmer.
You might as well do it:
public static Optional empty() {
return Optional.empty();
}
And we return to the era of Java 1.4, where there were no generics, but in compensation Casts and errors of ClassCastException
appeared everywhere.
Related: Differences between <T> and <?>
– Math