"final" final во время выполнения?

Я играл с ASM , и я считаю, что мне удалось добавить модификатор final к полю экземпляра класса; однако затем я приступил к созданию экземпляра указанного класса и вызвал для него сеттер, который успешно изменил значение поля now-final. что-то не так с моим изменением байт-кода, или это принудительно выполняется только компилятором Java?

Обновление: (31 июля) Вот вам код. Основными частями являются

  1. простой POJO с private int x и private final int y ,
  2. MakeFieldsFinalClassAdapter, который делает каждое поле, которое он посещает, окончательным, если оно еще не ,
  3. и AddSetYMethodVisitor, который заставляет метод setX () объекта POJO также устанавливать y в то же значение, что и x.

Другими словами, мы начинаем с класса с одним финалом (x) и одно незавершенное (y) поле. Делаем x финальным. Мы делаем setX () set y в дополнение к настройке x. Мы бегаем. Оба x и y устанавливаются без ошибок. Код находится на github . Вы можете клонировать его с помощью:

git clone git://github.com/zzantozz/testbed.git tmp
cd tmp/asm-playground

Обратите внимание на два момента: Причина, по которой я задал этот вопрос в первую очередь: и поле, которое я сделал окончательным, и поле, которое уже было окончательным, могут быть установлены с тем, что я считаются обычными инструкциями по байт-коду.

Другое обновление: (1 августа) Протестировано с 1.6.0_26-b03 и 1.7.0-b147 с теми же результатами. То есть JVM с радостью изменяет поля final во время выполнения.

Окончательное (?) Обновление: (19 сентября) Я удаляю полный исходный код из этого сообщения, потому что он был довольно длинным, но он все еще доступен на github (см. Выше).

Я считаю, что окончательно доказал , что JDK7 JVM нарушает спецификацию . (См. отрывок из ответа Стивена .) После использования ASM для изменения байт-кода, как описано ранее, я записал его обратно в файл класса. Используя превосходный JD-GUI , этот файл класса декомпилируется до следующего кода:

package rds.asm;

import java.io.PrintStream;

public class TestPojo
{
  private final int x;
  private final int y;

  public TestPojo(int x)
  {
    this.x = x;
    this.y = 1;
  }

  public int getX() {
    return this.x;
  }

  public void setX(int x) {
    System.out.println("Inside setX()");
    this.x = x; this.y = x;
  }

  public String toString()
  {
    return "TestPojo{x=" +
      this.x +
      ", y=" + this.y +
      '}';
  }

  public static void main(String[] args) {
    TestPojo pojo = new TestPojo(10);
    System.out.println(pojo);
    pojo.setX(42);
    System.out.println(pojo);
  }
}

Краткий взгляд на него должен сказать вам, что класс никогда не будет компилироваться из-за переназначения последнего поля, но все же запуск этого класса на простом ванильном языке JDK 6 или 7 выглядит так:

$ java rds.asm.TestPojo
TestPojo{x=10, y=1}
Inside setX()
TestPojo{x=42, y=42}
  1. Кто-нибудь еще внесет свой вклад, прежде чем я сообщу об этом?
  2. Может ли кто-нибудь подтвердить, должно ли это быть ошибкой в ​​JDK 6 или только в 7?

24
задан Community 23 May 2017 в 11:53
поделиться