Advertisements

Java Generics: Part 2

Generic functions

Generic functions work very similar to generic classes, except the generic type is on a per-call basis, and is determined by the parameters passed to the equation. Here’s the basic syntax:

private <T> T returnThis(T param) {
   return param;
}

Now, this is a fairly contrived example, but stick with me. Inside the angle brackets is our generic type declaration, just like we did on our list example. This just says, “We are going to use a generic type here”. Next, you can see that we’ve set our return type to T, so this function will return an instance of whatever T is. Finally, we have a parameter of type T called “param”. This is what determines the type of T. If the consumer passes in a String, then T is String, and the function will return a String. If they pass in a Button, then the function will return a Button.

Lets take a look at a much more useful example:

private <T> T first(List<T> param) {
   return param.get(0);
}

The “first” function will return the first element of param, regardless of what kind of list it is, and its strongly typed. So if you pass in an ArrayList<String>, it will return the first String. If you pass in a LinkedList<Button>, it will return the first Button. There are more sophisticated uses for this, but you can see that this is a useful tool.

There is another way to use generic functions that is, in my opinion, much more useful and you will definitely run into while using Android. Lets say that you have a function that needs to base its behavior on a generic type, but you don’t need/have an instance of that object. A prime example of this is a json parser, or a typeless dictionary. Rather than creating a method getString, and getInt, and getButton, and getLayout, etc., you can take a Class as a parameter, and return the type for that class:

private <T> T get(String name, Class<T> type){
   // find the value...
   return type.cast(value);
}

I have left out the details for actually retrieving the value, but you can see my point. By accepting a Class<T> as a parameter, your function can return a T. This keeps things very flexible for the user. For instance they might use it like this:

String s = get("name", String.class);
Button b = get("button", Button.class);

Now this isn’t super pretty, but that’s how java handles it. If you come from a C# world the syntax would look more like get<String>(“name”) or get<Button>(“button”), which I think is much nicer, but every language is different.

This gives you great type safety you wouldn’t get if your method returned an Object. A great example of a library that does exactly this is the GSON json parsing library (which I suggest you look into if you’re working with webservices).

Wildcards and enforcing inheritance

When you declare a class or method what uses generics, you can specify that the specified type must inherit from a particular class or interface. To do this you use a wildcard (?) and the “extends” keyword.

private <T extends Serializable> void putSerializable(T a) {
   // ...
}

Look at the example above. By specifying “extends Serializable” you are telling the compiler that the parameter passed to the “putSerializable” method must implement the Serializable interface. This can also be done with classes like View, Button, or even other generic classes like List<String>. If the consumer attempts to pass a non-serializable object to this method, the compiler will complain and report an error.

You can also specify flexible types when your methods take generic types as parameters, and can likewise enforce inheritance rules:

private void putCollection(Collection<Serializable> a) {
   // ...
}

private void putCollection(Collection<?> a) {
   // ...
}

private void putCollection(Collection<? extends Serializable> a) {
   // ...
}

Take a look at these three versions of the “putCollection” method. The first states that a MUST be a Collection<Serialiable>. It cannot be anything else. The second declaration states that a can be a collection of any kind of object, we don’t care what kind. The third stats that a can by any kind of collection who’s generic type implements the Serializable interface. The difference is subtle, but important.

Type erasure

So how does this work? How is it that the system can determine the type at runtime and resize the data allocation as needed? The truth is, it doesn’t. Generics are not actually included in the built application in Java. There are some languages that do, but java doesn’t which creates some limitations.

So if generics aren’t in the built application, what do they do? Generics serve as a COMPILE TIME check for type safety. When your application is built, the Java compiler replaces all references to your generic type with Object. This is why in our list example we stored our objects in an Object[]. You can’t create an array of T because T isn’t actually in the compiled code.

This also means that you cannot use introspection to look at the class of a generic type (T.class) and you cannot use the instanceof keyword for type safety. If you want to enforce inheritance rules or guarantee that the generic type implements a particular interface, you’ll have to use the techniques describe above in “Wildcards and enforcing inheritance”.

Advertisements