11

Are following two signatures same?

public static <T> void work(Class<T> type, T instance);

and

public static <T, S extends T> void work(Class<T> type, S instance);
Jin Kwon
  • 20,295
  • 14
  • 115
  • 184
  • 4
    No obviously not.They both are different. – JDGuide Jul 30 '13 at 04:52
  • Mmmmm...I actually think they might be equivalent. (Disbelievers: give an example accepted by one and not the other.) – Louis Wasserman Jul 30 '13 at 04:53
  • @LouisWasserman `work(Object.class, new Object())` and `work(Object.class, "Hello world!");` – Luiggi Mendoza Jul 30 '13 at 04:56
  • @LuiggiMendoza: I claim both of those are accepted by both methods. A `String` can be automatically upcast to an `Object` ;) – Louis Wasserman Jul 30 '13 at 05:01
  • @LuiggiMendoza Tested on JRE 1.7.0_02-b13 on Windows x64, both of those compile and run just fine. – Robert Rouhani Jul 30 '13 at 05:03
  • @LouisWasserman I tested this to make sure and compiles and runs. Now I'm intrigued on what's hapenning here (apart of the upcast). – Luiggi Mendoza Jul 30 '13 at 05:08
  • @LouisWasserman: Any call that uses explicit type parameters would obviously be an example, as using the wrong number of parameters will cause compile errors. – Mark Peters Jul 30 '13 at 05:18
  • @LouisWasserman they are equivalent post-type-erasure. You can prove this by trying to add both of them to the same class file and compiling it. –  Jul 30 '13 at 05:35

3 Answers3

11

No, the two signatures are not the same. From the Java Language Spec, Chapter 8:

Two methods have the same signature if they have the same name and argument types.

Two method or constructor declarations M and N have the same argument types if all of the following conditions hold:

  • They have the same number of formal parameters (possibly zero)
  • They have the same number of type parameters (possibly zero)

...

Since your two methods do not share the same number of type parameters, the signatures are not the same.

In practical cases where the methods are called with implicit type parameters, they might be seen as interchangeable. But this is only at the source level, never at the binary level. In other words, if you had the one-type-parameter version of work() in class Foo and it was called by a method in class Bar, and then you switched to the two-type-parameter version of work() and recompiled Foo, you would also need to recompile Bar.

Edit

@onepotato asks:

If they don't have the same signatures, then why when I copy and paste them in one class Eclipse tell me that they have the same method signature?

There is a difference between two signatures being equal and two signatures conflicting (being "override-equivalent"). Two signatures conflict if one is a subsignature of the other. This is explained later in the same section:

Two method signatures m1 and m2 are override-equivalent iff either m1 is a subsignature of m2 or m2 is a subsignature of m1.

It is a compile-time error to declare two methods with override-equivalent signatures in a class.

The signature of a method m1 is a subsignature of the signature of a method m2 if either:

  • m2 has the same signature as m1, or
  • the signature of m1 is the same as the erasure (§4.6) of the signature of m2.
Mark Peters
  • 80,126
  • 17
  • 159
  • 190
  • If they don't have the same signatures, then why when I copy and paste them in one class Eclipse tell me that they have the same method signature? I'm using JRE 1.5 – Bnrdo Jul 30 '13 at 06:45
  • @onepotato: I've added an explanation of that. – Mark Peters Jul 30 '13 at 14:50
3

Just looking at the bytecode we can see that they do not:

For the first:

// access flags 0x9
// signature <T:Ljava/lang/Object;>(Ljava/lang/Class<TT;>;TT;)V
// declaration: void work<T>(java.lang.Class<T>, T)
public static work(Ljava/lang/Class;Ljava/lang/Object;)V
 L0
  LINENUMBER 86 L0
  RETURN
 L1
  LOCALVARIABLE type Ljava/lang/Class; L0 L1 0
  // signature Ljava/lang/Class<TT;>;
  // declaration: java.lang.Class<T>
  LOCALVARIABLE instance Ljava/lang/Object; L0 L1 1
  // signature TT;
  // declaration: T
  MAXSTACK = 0
  MAXLOCALS = 2

For the second:

// access flags 0x9
// signature <T:Ljava/lang/Object;S:TT;>(Ljava/lang/Class<TT;>;TS;)V
// declaration: void work<T, ST>( extends java.lang.Class<T>, S)
public static work(Ljava/lang/Class;Ljava/lang/Object;)V
 L0
  LINENUMBER 86 L0
  RETURN
 L1
  LOCALVARIABLE type Ljava/lang/Class; L0 L1 0
  // signature Ljava/lang/Class<TT;>;
  // declaration: java.lang.Class<T>
  LOCALVARIABLE instance Ljava/lang/Object; L0 L1 1
  // signature TS;
  // declaration: S
  MAXSTACK = 0
  MAXLOCALS = 2
  • I'm sorry I'm not familiar with bytecode but. I stored two texts without `//` lines and `$ diff` said they are same. Aren't those `//` lines comments? – Jin Kwon Jul 30 '13 at 08:17
  • @JinKwon they are, but those are the proper signatures. If you reference the method in a different class, then change the signature, you will have to recompile the other class too. If the signatures were the same, that wouldn't be needed. –  Jul 30 '13 at 08:19
1

For any set of arguments, if a valid choice of T exists for

<T> void work(Class<T> type, T instance)

then a valid choice of T and S exists for

<T, S extends T> void work(Class<T> type, S instance)

and vice versa.

So from a theoretical point of view, they are equivalent. However, due to limited inference, it may be the case that, when type arguments are not given, one will compile while the other doesn't for certain arguments. In such a case, it is always possible to explicitly specify a valid set of type arguments for the case that doesn't compile, to make it compile.

Since they are equivalent, an API should always prefer the simpler form, i.e. without the S.

newacct
  • 119,665
  • 29
  • 163
  • 224