Reputation: 122432
This works to return a list of ints:
public List<Integer> GetIListImpl() {
return new ArrayList<Integer>();
}
But what if I want to let the caller specify the generic type? Something like this, although syntactically I'm not sure how to do it:
public List<T> GetIListImpl<T>() {
return new ArrayList<T>();
}
The usage would be:
List<String> = GetIListImpl<String>();
Upvotes: 17
Views: 10316
Reputation: 383726
static
factory methods for parameterized typesIt looks like you want to write convenient factory methods to instantiate generic collections.
You can write generic methods like these:
public static <T> List<T> newArrayList() {
return new ArrayList<T>();
}
public static <K,V> Map<K,V> newHashMap() {
return new HashMap<K,V>();
}
Then you can simply write:
// absolutely type-safe!!! no compilation warnings at all!!!
List<String> names = newArrayList();
List<Integer> nums = newArrayList();
Map<String, List<String>> map = newHashMap();
Note that in some contexts, the above methods do not have to be static
, and you may opt to leave out the implementation class
names out of the methods and only use the interface
names (e.g. newList
, newMap
).
This kind of generic type-inferring static
factory method is actually endorsed by Effective Java 2nd Edition; it had the unique privilege of being the very first item discussed in the book.
Here are the relevant quotes from Item 1: Consider static
factory methods instead of constructors:
A fourth advantage of
static
factory methods is that they reduce the verbosity of creating parameterized type instances.When you invoke the constructor of a parameterized class, unfortunately you must specify the type parameters even if they're obvious from context. This typically requires you to provide the type parameters twice in quick succession:
Map<String,List<String>> m = new HashMap<String,List<String>>();
This redundant specification quickly becomes painful as the length and complexity of the type parameters increase. With
static
factories, however, the compiler can figure out the type parameters for you. This is known as type inference. For example, suppose thatHashMap
provided thisstatic
factory:public static <K,V> HashMap<K,V> newInstance() { return new HashMap<K,V>(); }
Then you could replace the wordy declaration above with this succinct alternative:
Map<String,List<String>> m = HashMap.newInstance();
Unfortunately the standard collection implementations such as
HashMap
do not havestatic
factory methods as of release 1.6, but you can put these methods in your own utility class. More importantly you can provide suchstatic
factories in your own parameterized classes.
The item also prescribes the common naming convention for these static
factory methods:
getInstance
- returns an instance that is described by the parameters […]newInstance
- LikegetInstance
, except it guarantees that each instance returned is distinct from all others.new
Type
- LikenewInstance
, but used when the factory method is in a different class.Type
indicates the type of object returned by the factory method.
You do not have to explicitly provide the type parameters in most cases, since the Java generics type inference system can usually figure out what you need.
Nevertheless, to provide explicit type parameters, the syntax is to put it before the method name (not after). Here's an example of invoking with explicit parameter the generic method <T> List<T> emptyList()
from java.util.Collections
:
Collections.<String>emptyList();
// Collections.emptyList<String>(); // DOES NOT COMPILE
Note that a syntax quirk of the explicit type parameterization for generic method invocation is that you must qualify the type (if static
) or the object that you're invoking the method on, even if they can be omitted if it was not an explicit parameterization.
It should be noted that Guava in fact already provides the static
factory methods for the types in Java Collections Framework:
From the main package com.google.common.collect
:
Lists.newArrayList()
, newLinkedList()
, …Sets.newHashSet()
, newTreeSet()
, newEnumSet(…)
, …Maps.newHashMap()
, newTreeMap()
, newEnumMap(…)
, …In fact, in the spirit of Effective Java 2nd Edition recommendation, Guava's own collections do not provide public
constructors, but instead provide static create()
factory methods:
HashMultiSet.create()
, a Multiset
implementationTreeMultimap.create()
, a Multimap
implementationThe rest of the library also provides many highly useful functionality.
Upvotes: 25
Reputation: 15656
First you have to specify the generic type on the method and not the class level.
class Test
{
public static <T> List<T> getListImpl() {
return new ArrayList<T>();
}
}
Look at the position of the Generic type argument, it stands before the method name. For classes it stands behind the class name. So instead of adding the generic type after the method name you call it like this
List<Integer> l = Test.<Integer>getListImpl()
You have to call it with either the class name or an instance. The compiler does not like the following:
List<Integer> l = <Integer>getListImpl()
Sometimes it works without the generic type
List<Integer> l = getListImpl()
Here the comparison class and method generic
class Test<T>{}//Generic after class name
T<Integer> t = new T<Integer>();
void <T> methName(){}//Generic before method name
t.<Integer>methName();
Upvotes: 1
Reputation: 93157
The syntax would be
public static <T> List<T> getIListImpl() {
return new ArrayList<T>();
}
You were close.
And the usage is more like :
MyClass.<Integer>getIListImpl();
Upvotes: 8