10
public static <T> T foo(T x, T x2) {
    return (T) (x + "   " + x2);
}

public static void main(String args[]) {
    System.out.println(foo(33, "232"));
}

I know T gets to be of the type that is passed in the parameter. But here there are two types. Which one of them is T?

and why the compiler doesn't force me to have the parameters of same type when I call foo?

TheLogicGuy
  • 682
  • 8
  • 19
  • 12
    It's legal because `T` can be `Object`, so you can pass the method any two objects. (`32` can be an `Integer` rather than an `int` in this case.) – khelwood Feb 09 '17 at 10:20
  • 1
    And the upper bound of `T` is `Object`, so in the compiled code, the cast `(T)` is `(Object)`, which is valid, because `String` is an `Object`. – Andy Turner Feb 09 '17 at 10:23
  • 1
    Also to add on the comments, that´s the reason why you never want to make use of undbound types(leaving out the generics), as basicly every combination of classes is valid due to it. When you do it, you might find yourself in a sitatation where `ClassCastExceptions` are the usual result of trying to work with the raw types – SomeJavaGuy Feb 09 '17 at 10:25
  • 2
    @KevinEsche by "raw" do you mean "unbounded"? If so, I would say that, because "raw" means something specific in the context of generics. – Andy Turner Feb 09 '17 at 10:25
  • The compiler _does_ give you an "Unchecked cast from String to T" warning in `foo()`, essentially telling that you shouldn't do this. – Mick Mnemonic Feb 09 '17 at 10:30
  • IntelliJ IDEA inferred type `java.io.Serializable` with such arguments.. – cybersoft Feb 09 '17 at 10:42
  • 2
    @cybersoft yeah, it would be for all compilers, because both `String` and `Integer` are `Serializable`, which is more specific than `Object`. But the point is that `T` doesn't mean "the same type", it means "some common type". – Andy Turner Feb 09 '17 at 10:44
  • This question is sort-of related: [Why are JUnit assert methods not generic in Java?](http://stackoverflow.com/questions/38787083/why-are-junit-assert-methods-not-generic-in-java) – Andy Turner Feb 09 '17 at 11:02

2 Answers2

16

It's legal because Integer and String have a common base type. T can be Object, so foo(T, T) will accept any two objects. 32 can be autoboxed to an Integer, which is a kind of Object. "232" is a String, which is a kind of Object.

(As pointed out by comments, for Integer and String, there is a closer common base-type, Serializable; but the principle is the same whichever base type the compiler resolves T to.)

khelwood
  • 55,782
  • 14
  • 81
  • 108
  • Can you describe what happens in this line: `return (T) (x + " " + x2)`? Is it equivalent to `return (Object) (x.toString() + " " + x2.toString())`? If so, what happens in the casting, if it casts a string to object then how this string is retrieved in the printing? – TheLogicGuy Feb 09 '17 at 13:54
  • 1
    @TheLogicGuy If you try and add any object to a string, it be converted using `toString()`. If you try and print any object, it will convert be converted to a String using `toString()`. (If your object is a string, `toString()` just returns `this`.) – khelwood Feb 09 '17 at 13:58
5
public static <T> T foo(T x, T x2) {
    return (T) (x + "   " + x2);
}

It should be noted that whilst this compiles, it can fail at runtime:

int f = foo(1, 2);

will fail Ideone demo, because the method returns a String, not an Integer.

It compiles, though, because type erasure means that T is rewritten to the upper bound, namely Object. So:

public static Object foo(Object x, Object x2) {
  return (Object) (x + "   " + x2);
}

This is fine, because the result is a String, which is a subtype of Object.

I guess this is just a case where the generics model it waiting to catch you out. This is why I would avoid mixing generics and explicit casting.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243