Подтвердить что ты не робот

Как сделать 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? И все-таки можно как-то решить оригинальную загадку?

4b9b3361

Ответ 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;