It is possible to generic your own Java classes. Generics is not restricted to the predefined classes in the Java API's.
Here is a simple example:
public class GenericFactory<E> { Class theClass = null; public GenericFactory(Class theClass) { this.theClass = theClass; } public E createInstance() throws IllegalAccessException, InstantiationException { return (E) this.theClass.newInstance(); } }
The
<E>
is a type token that signals that this class can have a type set when instantiated.
Here is an example of how:
GenericFactory<MyClass> factory = new GenericFactory<MyClass>(MyClass.class); MyClass myClassInstance = factory.createInstance();
Notice how it is not necessary to cast the object returned from the
factory.createInstance()
method.
The compiler can deduct the type of the object from the generic type of the
GenericFactory
created, because you specified the type inside the <>.
Each instance of the
GenericFactory
can be generified to different types. Here are two examples:GenericFactory<MyClass> factory = new GenericFactory<MyClass>(MyClass.class); MyClass myClassInstance = factory.createInstance(); GenericFactory<SomeObject> factory = new GenericFactory<SomeObject>(SomeObject.class); SomeObject someObjectInstance = factory.createInstance();
Generics in Methods
It is possible to generify methods in Java. Here is an example:public static <T> T addAndReturn(T element, Collection<T> collection){ collection.add(element); return element; }This method specifies a type T which is used both as type for theelement
parameter and the generic type of theCollection
. Notice how it is now possible to add elements to the collection. This was not possible if you had used a wildcard in theCollection
parameter definition.So, how does the compiler know the type of T?The answer is, that the compiler infers this from your use of the method. For instance:String stringElement = "stringElement"; List<String> stringList = new ArrayList<String>(); String theElement = addAndReturn(stringElement, stringList); Integer integerElement = new Integer(123); List<Integer> integerList = new ArrayList<Integer>(); Integer theElement = addAndReturn(integerElement, integerList);Notice how we can call theaddAndReturn()
method using bothString
's andInteger
's and their corresponding collections. The compiler knows from the type of theT
parameter andcollection<T>
parameter definitions, that the type is to be taken from these parameters at call time (use time).The compiler can even perform more advanced type inference. For instance, the following call is also legal:String stringElement = "stringElement"; List<Object> objectList = new ArrayList<Object>(); Object theElement = addAndReturn(stringElement, objectList);In this case we are using two different types for T:String
andObject
. The compiler then uses the most specific type argument that makes the method call type correct. In this case it infers T to beObject
.The inverse is not legal though:Object objectElement = new Object(); List<String> stringList = new ArrayList<String>(); Object theElement = addAndReturn(objectElement, stringList);In this case the compiler infers, that for the method call to be type safe, T must be aString
. TheobjectElement
passed in for theT element
parameter must then also be aString
(and it isn't). Therefore the compiler will report an errorClass Objects as Type LiteralsClass objects can be used as type specifications too, at runtime. For instance, you can create a generified method like this:public static <T> T getInstance(Class<T> theClass) throws IllegalAccessException, InstantiationException { return theClass.newInstance(); }Here are a few examples of calls to thegetInstance()
method:String string = getInstance(String.class); MyClass myClass = getInstance(MyClass.class);As you can see the return type changes depending on what class object you pass in as parameter to the method. This can be quite handy in database API's like Butterfly Persistence where you read objects from a database. Here is an example method definition:public static <T> T read(Class<T> theClass, String sql) throws IllegalAccessException, InstantiationException { //execute SQL. T o = theClass.newInstance(); //set properties via reflection. return o; }Here is how you would call theread()
method:Driver employee = read(Driver.class, "select * from drivers where id=1"); Vehicle vehicle = read(Vehicle.class, "select * from vehicles where id=1");
No comments:
Post a Comment