5

Preface: I understand generics and how they're declared at the class level (e.g. class MyClass<T>) but I've never seen it declared at the level of a static method, and without any explicit bindings (e.g. class MySubclass<String> extends MyClass).

I found this code snippet in an app I'm working on (I didn't write this part). I've never seen a method declared this way. <T> is not defined anywhere else in the class. Intent.getExtras().get() returns an Object which may actually be a String, Boolean ...etc.

private static <T> T getItemExtra(final Intent intent, final String extraName) {
    T item = null;

    if(intent != null && intent.getExtras() != null) {
        item = (T) intent.getExtras().get(extraName);
    }

    return item;
}

Sample usage:

String s1 = getItemExtra(someIntent, "some_string_extra");
Uri u1 = getItemExtra(someIntent, "some_uri_extra");

How does the JVM know what type to use for <T>? (Yes this method compiles and executes successfully).

Barry Fruitman
  • 12,316
  • 13
  • 72
  • 135

3 Answers3

3

How does the JVM know what type to use for <T>?

The basic answer is, it doesn't. In Java, generic types are used by the type checker at compile time, but are removed from the program when the program actually runs. So for instance, the cast to T in item = (T) intent.getExtras().get(extraName) doesn't actually do anything at runtime: it's effectively a cast to Object, always, regardless of what type the caller expects T to be. (This is different from a cast to a normal type like String, where the program will fail with an exception immediately if you try to cast the wrong thing.)

The fact that you can cast to T without a check is a loophole in Java's type system that can cause weird class-cast exceptions. For instance, if you say

String s = getItemExtra(...); 
s.toLowerCase();

but getItemExtra doesn't return a string, then you'll get an exception on the second line telling you that s isn't a string, even though there's no cast on that line. For that reason, when the Java compiler sees a cast to T it will generate an unchecked-cast warning, telling you that it can't check that your typecast was legal and you might run into problems like this.

Barry Fruitman
  • 12,316
  • 13
  • 72
  • 135
jacobm
  • 13,790
  • 1
  • 25
  • 27
1

T is what is known as a type interface. In this instance, the return type is a type interface so simply saying that getItemExtra(...) will return a String or Uri is enough for the compiler to know that this method will return that object. That being said, this isn't the best example to be introduced to type interfaces so here is an easier example.

ArrayList is implemented using a type interface except you are telling the ArrayList what object type it will contain, except instead of T they are using E, which is fine because the character is arbitrary so long as you use that same character throughout your implementation.

Here is a relevant snippet from the actual implementation:

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{

So if you declare ArrayList<String> the compiler will replace E with String at compile time. Same goes for ArrayList<YourCustomObject> it will replace E with YourCustomObject throughout the implementation.

CodyEngel
  • 1,501
  • 14
  • 22
1

That is what is known in Java as a generic method. The T in represents a type, which you would normally specify. The compiler would then replace any instances of T with the type you replaced it with. If you don't, the compiler will attempt to infer the correct type from the available information.

You should look through the Java Generics Tutorial for a more in depth explanation.

phxhawke
  • 2,581
  • 23
  • 17