Я играл с ASM, и я считаю, что мне удалось добавить окончательный модификатор в поле экземпляра класса; однако затем я начал создавать экземпляр указанного класса и вызывать на нем сеттер, который успешно изменил значение поля, которое теперь является окончательным. Я что-то делаю неправильно с моими изменениями в байткоде или окончательно соблюдаю только компилятор Java?
Обновление: (31 июля) Вот вам какой-то код. Основные части
- простое POJO с
private int x
иprivate final int y
, - MakeFieldsFinalClassAdapter, который делает каждое поле посещением окончательного, если оно уже отсутствует,
- и AddSetYMethodVisitor, что приводит к тому, что метод setX() POJO также устанавливает y в то же значение, что и x.
Иными словами, мы начинаем с класса с одним конечным (x) и одним не конечным (y) полем. Мы делаем x final. Мы устанавливаем setX() множество 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 с радостью изменяет окончательные поля во время выполнения.
Финальное (?) обновление: (19 сентября) Я удаляю полный источник из этого сообщения, потому что он довольно длинный, но он все еще доступен на github (см. Выше).
Я считаю, что я убедительно доказал, что JVK7 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}
- Есть ли кто-нибудь еще, прежде чем сообщать об ошибке?
- Кто-нибудь может подтвердить, что это ошибка в JDK 6 или только в 7?