Что делает компилятор Java с несколькими общими рамками?
Посмотрите на этот (возможно, глупый) код:
public <T extends Appendable & Closeable> void doStuff(T object)
throws IOException{
object.append("hey there");
object.close();
}
Я знаю, что компилятор удаляет общую информацию, поэтому мне интересен код Java 1.4, эквивалентный тому, что делает компилятор (я уверен, что компилятор не переупорядочивает исходный код, поэтому я прошу эквивалент Java-версия, которую наивные люди, подобные мне, могут понять)
Это что-то вроде этого:
public void doStuff(Object object)
throws IOException{
((Appendable)object).append("hey there");
((Closeable)object).close();
}
Или, скорее, так:
public void doStuff(Object object)
throws IOException{
Appendable appendable = (Appendable) object;
Closeable closeable = (Closeable) object;
appendable.append("hey there");
closeable.close();
}
Или даже так:
public void doStuff(Appendable appendable)
throws IOException{
Closeable closeable = (Closeable) appendable;
appendable.append("hey there");
closeable.close();
}
Или еще одна версия?
Ответы
Ответ 1
Подпись метода выглядит как public void doStuff(Appendable appendable)
, потому что
Порядок типов в оценке только существенное в том, что стирание переменной типа определяется первым типом в своей привязке и что переменная типа или типа класса может появляться только в первом должность.
(JLS §4.4 Переменные типа)
Это поведение может быть важным, если вы используете отражение для доступа к этому методу.
Другое использование этого поведения заключается в сохранении двоичной совместимости с предварительными интерфейсами, как описано в Учебник по основам, раздел 10 (спасибо Mark Peters за указание на это). То есть
public static <T extends Object & Comparable<? super T>> T max(Collection<T> coll)
является бинарным, совместимым с его исходной версией, возвращающей Object
.
Тело метода эквивалентно следующему, но я думаю, что его детали реализации:
appendable.append("hey there");
((Closeable) appendable).close();
Ответ 2
Я не мог дождаться, мне нужно было ответить на свой вопрос. Ответ представляет собой комбинацию моей первой и третьей версий: первая граница используется как переменная типа, и объект присылается ко второй привязки всякий раз, когда это необходимо. Это результирующий байт-код (я добавил один разрыв строки для удобочитаемости):
// Method descriptor #20 (Ljava/lang/Appendable;)V
// Signature: <T::Ljava/lang/Appendable;:Ljava/io/Closeable;>(TT;)V
// Stack: 2, Locals: 2
public void doStuff(java.lang.Appendable object) throws java.io.IOException;
0 aload_1 [object]
1 ldc <String "hey there"> [26]
3 invokeinterface java.lang.Appendable.append(java.lang.CharSequence) :
java.lang.Appendable [28] [nargs: 2]
8 pop
9 aload_1 [object]
10 checkcast java.io.Closeable [34]
13 invokeinterface java.io.Closeable.close() : void [36] [nargs: 1]
18 return
Line numbers:
[pc: 0, line: 14]
[pc: 9, line: 15]
[pc: 18, line: 17]
Local variable table:
[pc: 0, pc: 19] local: this index: 0 type: rumba.dumba.Bumba
[pc: 0, pc: 19] local: object index: 1 type: java.lang.Appendable
Local variable type table:
[pc: 0, pc: 19] local: object index: 1 type: T