Как сделать E1 + = E2 незаконным, в то время как E1 = E1 + E2 является законным?
Я читал 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? И все-таки можно как-то решить оригинальную загадку?
Ответы
Ответ 1
Попробуйте с javac < 1.4.2, он тоже будет работать.
Это было изменение между разными версиями.
Изменение для 1.4.2 (x + = i; разрешено раньше, а не с):
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4642850
Это правильно, потому что версия JLS 2. определена:
Все составные операторы присваивания требуют, чтобы оба операнда были примитивного типа, кроме + =, что позволяет правому операнду быть любого типа, если левый и правый операнд имеет тип String.
Изменение для 7 (x + = i; недопустимое до, разрешено с):
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4741726
Что правильно, так как версия JLS 3. (см. http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.26 предыдущее необходимое условие удалено)
Просто небольшое редактирование: я не вижу способа исправить/решить головоломку в Java 7.0_10
Ответ 2
У меня есть следующее и оно отображает данное решение как правильный ответ:
public class testIt
{
public static void main(String args[])
{
new testIt();
}
public testIt()
{
Object x = "Buy";
String i = "Effective Java!"
x += i;
x = x + i;
}
}
когда я скомпилирую это, я получаю
testIt.java: incompatible types
found: java.lang.Object
required: java.lang.String;
x += i;
^
1 error
Ответ 3
В Java 6 вы можете сказать
Object x = 1;
String i = "i";
x = x + i; // compiles
x += i; // doesn't compile in Java 6, but does in Java 7.
System.out.println(x);
Почему это происходит?
То же, что и
x[0] = x[0] + i;
Скомпилирован с Java 7
Object[] x = {new Object()};
String i = "Effective Java!";
x[0] += i;
System.out.println(x[0]);
печатает
[email protected] Java!
но не с обновлением Java 6 37
Error:Error:line (25)java: src\Main.java:25: incompatible types
found : java.lang.Object
required: java.lang.String
Означает ли это, что компилятор Java отклоняется от JLS?
Я подозреваю, что Java 6 не соответствует текущей JLS. Может быть более старая версия, которой она действительно соответствовала.
И все-таки можно как-то решить оригинальную загадку?
Есть подсказка. Это компилирует
char ch = '0';
ch *= 1.1;
Это не
char ch = '0';
ch = ch * 1.1;