Я читал Java Puzzlers у Блоха и Гафтера и добрался до головоломки 10 (Tweedledee). Суть этой головоломки заключается в
предоставлять декларации для переменных
x
иi
таким образом, что это юридическое утверждение:x = x + i;
но это не так:
x += i;
Решение этой задачи выглядит, согласно книге, следующим образом:
Object x = "Buy ";
String i = "Effective Java!";
В книге утверждается, что в операторе +=
правое выражение может быть любого типа, только если левое выражение имеет тип String
. Тем не менее, я попытался запустить этот код, и он скомпилирован и работает без проблем.
Затем я вникнул в спецификацию языка Java. В разделе 15.26.2 говорится о двух случаях: когда левое выражение является выражением доступа к массиву, а когда нет. Если выражение левого операнда не является выражением доступа к массиву, то JLS ничего не говорит о том, что левое выражение является строкой. Когда это произойдет, эта часть заключает в себе:
Если T является ссылочным типом, то он должен быть String. Поскольку класс String является конечный класс, S также должен быть String. Поэтому проверка времени выполнения, которая иногда требуемый для простого оператора присваивания, никогда не требуется для сложный оператор присваивания.
❖ Сохраненное значение компонента массива и значение правого операнд используются для выполнения двоичной операции (конкатенация строк) обозначенный оператором сопредельного присваивания (который обязательно + =). Если эта операция завершается внезапно, то выражение присваивания завершается внезапно по той же причине, и никакое присваивание не происходит.
T здесь тип левого операнда, определенный во время компиляции, а S - выбранный компонент массива. Поэтому я подумал, что я бы изменил свой код:
Object[] x = {new Object()};
String i = "Effective Java!";
x[0] += i;
Но даже этот код компилируется и запускается без проблем, даже если new Object()
даже удаленно не String
.
Почему это происходит? Означает ли это, что компилятор Java отклоняется от JLS? И все-таки можно как-то решить оригинальную загадку?