1

Say I have this code:

class Demo
{
    static <T> T pick(T a1, T a2)
    {
        return a2;
    }

    public static void main(String[] args)
    {
        pick("d", 123);
    }
}

From what I learned, it seems like I have stated that the two parameters a1, a2 and the return type of pick must be under the same generic type T.

So why is the compiler allowing me to pass a String and an Integer to pick?

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
jxie0755
  • 1,682
  • 1
  • 16
  • 35

2 Answers2

5

Both String and Integer are subclasses of Object, along with every other type in Java. When compiling a generic method (or class), Java attempts to find the closest supertype shared between every instance of the generic type. This part never fails, because Object exists. But if a generic type is resolved to Object, it may not be useful anymore.

So what’s the point in using generics here if the compiler will let any types to be used? It’s all to do with the return type. Assuming your definition of pick(), what do you think will happen when you try to compile each of these lines?

Object o = pick("Hello", 123);       // 1

String s = pick("Hello", 123);       // 2
String s = pick("Hello", "world");   // 3

Integer i = pick("Hello", 123);      // 4
Integer i = pick(123, 456);          // 5
int i = pick(123, 456);              // 6

1 compiles just fine, but then you’ve lost any useful type information. This is what would happen if you didn’t use generics at all, and instead just used Object for everything. It’s you’d have had to do before Java 5, along with plentiful casting and exception catching.

2 and 4 won’t compile:

error: incompatible types: inferred type does not conform to upper bound(s)

Since the two arguments to pick() share only Object as a common supertype, T becomes Object and an Object is returned and you can’t assign an Object to a String or an Integer.

3 works just fine though. Both arguments have the same type, so T is easily determined to be String. 5 works for similar reasons.

6 also works, but not because T becomes int. int is a primitive type, so it can’t be used in a generic. When attempting to resolve the generic type T, the compiler first autoboxes the primitive arguments into a ‘real’ class (Integer in this case). This also happens for 4 and 5, and even when you simply assign a literal like Integer i = 123;. The difference here is that the result (an Integer) is unboxed back to an int so that it can be assigned to i.


In your example implementation of pick(), the return value should have the same type as the second parameter. If your API specifies that the result is always derived primarily from that parameter, you could use two generic types:

static <T, U> T pick(U a1, T a2) {
    return a2;
}

With this addition, 2 still fails to compile, but 4 works as you’d expect.

MTCoster
  • 5,868
  • 3
  • 28
  • 49
  • thanks, I think **4** is specially useful, as one may think that it should work. Because the returned type should be an Integer. But, has to be the common class of both paratmeters. – jxie0755 Feb 11 '19 at 02:58
  • I want to ask, that if I am not familiar the inheritance tree of Java types, is there a way that it can tell me what is the under this situation? For example, I just learned that a `String` and an `Array`, the generic type is `Serializable`. But how would I know if I don't know such type exist? – jxie0755 Feb 11 '19 at 03:03
  • 1
    If you’re using an IDE, it may offer dynamic resolution of generic types for you. If you’re working by hand, the javadocs are your best bet. Let’s use the page for [`String`](https://docs.oracle.com/javase/8/docs/api/java/lang/String.html) as an example. You can see the inheritance structure at the top, right underneath the class name. A class such as [`CharBuffer`](https://docs.oracle.com/javase/8/docs/api/java/nio/CharBuffer.html) has a longer inheritance tree. However, that only helps with direct inheritance. Interfaces add an extra layer of complexity, but those details are there too. – MTCoster Feb 11 '19 at 03:07
  • Thanks a lot, I am using Intellij, I will try to figure that out. But at the same time, referring to the javadocs is always a good backup plan. – jxie0755 Feb 11 '19 at 03:10
  • There’s a working (AFAIK) snippet on [this question](https://stackoverflow.com/q/9797212/1813169) for determining the nearest common supertype programatically if you’re more into the DIY automation method :p – MTCoster Feb 11 '19 at 03:10
  • 1
    IntelliJ does indeed provide this functionality - just hold cmd/ctrl while mousing over the method call ([*screenshot*](https://i.stack.imgur.com/l8DpJ.png)) – MTCoster Feb 11 '19 at 03:14
2

The compiler will walk down the inheritance tree of a1 and a2 to find a common ancestor, in this case Object. Part of the reason you aren't seeing that is that you are discarding the return value. The following two versions won't compile:

String choice = pick("d", 123);

and

Integer choice = pick("d", 123);

The following will, however:

Object choice = pick("d", 123);
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
  • Thanks, I am learning this new knowledge. As someone who only knows python, you can see that it is difficult for me to understand statically typed language. – jxie0755 Feb 11 '19 at 02:46
  • What do you mean by statically typed? – Vishwa Ratna Feb 11 '19 at 02:48
  • @CommonMan https://stackoverflow.com/q/1517582/1813169 – MTCoster Feb 11 '19 at 02:51
  • @CommonMan From my shallow understanding, it means Java is very strict on object types, it requires claiming variable types and returned value types of a method. And one type does not transform to another type (unless casting). But python has no such restriction. – jxie0755 Feb 11 '19 at 02:55
  • 1
    @MTCoster. The accepted answer is somewhat misleading there. Python is statically, but implicitly typed. Java is both statically and explicitly. – Mad Physicist Feb 11 '19 at 02:56
  • @MadPhysicist Good point! We need a better canonical static/dynamic answer – MTCoster Feb 11 '19 at 02:57
  • @MTCoster Thanks for clearing that up. I am always confused what python exactly is.... – jxie0755 Feb 11 '19 at 03:12