Являются ли следующие две подписи одинаковыми?
public static <T> void work(Class<T> type, T instance);
и
public static <T, S extends T> void work(Class<T> type, S instance);
Являются ли следующие две подписи одинаковыми?
public static <T> void work(Class<T> type, T instance);
и
public static <T, S extends T> void work(Class<T> type, S instance);
Нет, две подписи не совпадают. Из Java Language Spec, глава 8:
Два метода имеют одну и ту же подпись, если они имеют одинаковые имена и типы аргументов.
Два объявления метода или конструктора M и N имеют одинаковые типы аргументов, если выполняются все следующие условия:
- Они имеют одинаковое количество формальных параметров (возможно, ноль)
- Они имеют одинаковое количество параметров типа (возможно, ноль)
...
Поскольку ваши два метода не имеют одинакового количества параметров типа, подписи не совпадают.
В практических случаях, когда методы вызываются с неявными параметрами типа, они могут рассматриваться как взаимозаменяемые. Но это только на исходном уровне, никогда на двоичном уровне. Другими словами, если у вас была версия типа work()
с одним типом в классе Foo
и она была вызвана методом в классе Bar
, а затем вы переключились на версию с двумя типами параметров work()
и перекомпилировать Foo
, вам также потребуется перекомпилировать Bar
.
@onepotato спрашивает:
Если у них нет одинаковых подписей, то почему, когда я копирую и вставляю их в один класс Eclipse, скажите мне, что они имеют одну и ту же подпись метода?
Существует разница между двумя сигнатурами, которые равны, и двумя сигнатурами, конфликтующими ( "эквивалентными" ). Две подписи конфликтуют, если одна из них является подсудностью другой. Это объясняется далее в том же разделе:
Две сигнатуры метода m1 и m2 эквивалентны переопределению, если m1 является поднаклейкой m2 или m2, является поднапряжением m1.
Ошибка компиляции для объявления двух методов с эквивалентными эквивалентами в классе.
Подпись метода m1 является подсигналом сигнатуры метода m2, если:
- m2 имеет ту же подпись, что и m1, или
- подпись m1 такая же, как стирание (§4.6) подписи m2.
Просто глядя на байт-код, мы видим, что они этого не делают:
Для первого:
// 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
Для второго:
// 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
Для любого набора аргументов, если допустимый выбор T
существует для
<T> void work(Class<T> type, T instance)
то действительный выбор T
и S
существует для
<T, S extends T> void work(Class<T> type, S instance)
и наоборот.
Итак, с теоретической точки зрения они эквивалентны. Однако из-за ограниченного вывода может случиться так, что если аргументы типа не заданы, они будут компилироваться, а другие - не для определенных аргументов. В таком случае всегда можно явно указать допустимый набор аргументов типа для случая, который не компилируется, чтобы его компилировать.
Поскольку они эквивалентны, API всегда должен выбирать более простой вид, т.е. без S
.