Requirements: To understand this theory you must be familiar with basic generics.
One final point of clarification. When defining static methods you often want to accept a generic type:
public static <Q> void myStaticMethod(List<Q> list) {
// ... do something with the list.
}
The first thing to notice is the odd placing of the generic type <Q>
.
Somewhere between public static
and void
. This means that this method
will use the generic type Q
somewhere in the definition that follows.
A second important point. The static method may be defined within a class that accepts a generic type, but has nothing to do with it. A more complete example would be:
public class MyGenericClass<T> {
private List<T> list;
// insert additional stuff here.
public static <Q> void myStaticMethod(List<Q> list) {
// ... do something with the list.
}
}
The static method may be used within the class itself, but may just as easily be called from the outside. That is why you should always use a different “generic type” when defining static methods.
Please note that the class that defines this method doesn’t even need to be generic:
public class MyUtilityClass {
// insert additional stuff here.
public static <Q> void myStaticMethod(List<Q> list) {
// ... do something with the list.
}
}
In other words: “But what if I really don’t care what is stored in that list.”
Really? Well if you’re sure. Please note that this Very Rarely happens. Let’s consider this functions:
public static String toString(List<?> myList) { ... }
We want to neatly print all the element in this list as a single String.
This syntax (with the ?
) is actually allowed. This function says:
“I accept any list, don’t really care what type the elements have.”
When using this list all elements will appear to us as Object
:
Object item = myList.get(i);
Since all reference types in Jave inherit from Object that works out.
Additionally, through polymorphism, we can all the toString()
on each element
and get the correct representation for each object, even though this function doesn’t need
to know the actual type.
If the function was defined like this, there would be problems.
public static String toString(List<Object> myList) { ... }
This requires the list to actually contain objects. So passing a list of Strings, Persons or any other type would not work.
Sometimes you may want to express the idea to store some kind of “Person” for instance.
Lets take this diagram:
classDiagram
Person <|-- Student
Person <|-- Teacher
Now we can of course define a list than can contain either of these
classes: List<Teacher>
or List<Student>
.
The only alternative so far is List<Person>
which can contain both.
Consider this definition:
class MyClass<T extends Person> {
private List<T> list;
public T getPerson(int index);
}
To use this class we can instantiate it in three ways:
new MyClass<Person>
new MyClass<Student>
new MyClass<Teacher>
The restriction T extends Person
will not allow you to do this:
new MyClass<Chicken>
because Chicken
will likely not extend Person
.
This class can function with either only Student
s or
only Teacher
s. The ability to work with a list of all possible types of Person
of course remains available.
<? extends Person>
This construction is used in the situation where you don’t care about actual type
since you are not going to refer to any methods implemented by descendants of the Person
class in this case.
Note: If at any point in the code you start using
instance of
, you clearly should not have chosen a generic solution.
The difference with just a <?> is that in this case all the Person
methods
are available to you. Not just the methods from Object
.
The user of your method can supply instances of Person
and any decendant,
but never String
s or other reference types.
This construction is used when your code requires a type definition that has a restriction that requires knowledge of that type.
<T extends Comparable<T>>
In this example we accept any type T
, as long as that type can
be compared against itself. It must implement the compareTo(T other)
method.
In order to accept Person
as an acceptable type, it should have been defined as:
public class Person implements Comparable<Person> {
// ...
@Override
public boolean compareTo(Person other) {
// ...
}
}
If Person
does not implement the Comparable<Person>
interface,
then it may not be used as a type for T
in this situation.
This construction is used, when two different types are required but the second type requires knowledge of the first type:
<T, L extends Iterable<T>>
This example requires an instance of T
and any type of Iterable<T>
I could write a very generic contains method:
public static <T, L extends Iterable<T>> boolean contains(T needle, L haystack) {
for (T item:haystack) {
if (item.equals(needle)) {
return true;
}
}
return false;
}
Please note that this example is a bit farfetched, since I could have easily used:
public static boolean contains(Object obj, Iterable<?> iterable) {
// ...
}
Keep in mind that a simple solution is always preferable.