Классы Anonymous-Inner показывают неправильный модификатор

Насколько я понимаю, следующий код должен был вывести true в качестве вывода.

Однако, когда я запустил этот код, он печатает false.

Из документов Java по Анонимные классы 15.9.5. :

Анонимный класс всегда неявно окончательный

public class Test {
    public static void main(String args[]) {
        Object o = new Object() {
        };
        System.out.println("Annonymous class is final: " + Modifier.isFinal(o.getClass().getModifiers()));
    }
}

Почему этот код ведет себя так?


person Sachin Sachdeva    schedule 03.01.2019    source источник
comment
Возможный дубликат Анонимные внутренние классы и модификатор Final   -  person miken32    schedule 06.05.2019
comment
@ miken32 Я отменил закрытое голосование по вашему предложению, потому что ответы на этот вопрос гораздо яснее, чем на вопрос, который вы предложили.   -  person Mark Rotteveel    schedule 07.05.2019


Ответы (4)


Обратите внимание, что с тех пор формулировка в JLS этого конкретного раздела значительно изменилась. Теперь (JLS 11) он гласит:

15.9.5. Объявления анонимных классов:

Анонимный класс никогда не является окончательным (§8.1.1.2).

Тот факт, что анонимный класс не является окончательным, имеет значение при приведении, в частности при преобразовании сужающей ссылки, разрешенном для оператора приведения (§5.5). Это также представляет интерес для создания подклассов, поскольку невозможно объявить подкласс анонимного класса, несмотря на то, что анонимный класс не является окончательным, потому что анонимный класс не может быть назван с помощью предложения extends (§8.1.4).

Это изменение формулировки было введено в JLS 9. Семантика анонимных классов и поведение методов в вопросе остались в основном без изменений, цель состояла в том, чтобы избежать путаницы, о которой идет речь в этом вопросе.

В заявке, вызвавшей изменение, говорится:

Давнее поведение javac, начиная с версии 1.3, по большей части заключалось в том, что не рассматривал классы как "окончательные". Чтобы устранить это несоответствие, необходимо изменить спецификацию, чтобы она точно отражала эталонную реализацию.

В частности, анонимные классы почти никогда не создаются с установленным флагом ACC_FINAL. Мы не можем изменить это давнее поведение, не повлияв на некоторых клиентов сериализации (это допустимо, но излишне разрушительно). И мы не можем точно реализовать Class.getModifers (который обещает предоставить модификаторы языка Java) без файлов классов, кодирующих модификаторы языка.

Однако изменение действительно фактически в некоторой степени изменило семантику, и это также было задокументировано в этой заявке как допустимое влияние:

Это изменение влияет на набор легальных программ, поскольку позволяет выполнять некоторые приведения типов, которые в текущей спецификации считаются незаконными (см. JDK-6219964). Но после поиска упоминаний о «финальных» классах в JLS я не ожидаю никакого другого воздействия, а это означает, что это исправление, совместимое с исходным кодом.

person Hulk    schedule 03.01.2019
comment
Хотя спецификация была обновлена, я не думаю, что старая противоречива — просто неясна. Анонимные классы неявно финальны в двух смыслах: по своей природе (разработчик не может этого избежать) и дискретно (не выражено напрямую - ни разработчиком, ни самим классом). - person Jacob Raihle; 03.01.2019
comment
Ага, старое доброе «изменить спецификацию, чтобы она соответствовала тому, что делает наша… реализация»… - person Holger; 09.01.2019
comment
Я не согласен с тем, что «Семантика анонимных классов… осталась неизменной». Java 8 не компилирует CharSequence c2 = (CharSequence)new Object() {};, в то время как Java 9 компилирует. Другими словами, семантика языка полностью изменилась ради сохранения совместимости с ошибкой, которая затронула только Reflection. - person Holger; 17.06.2020
comment
@Holger Я добавил цитату из билета - кажется, этот эффект был известен во время изменения и считался приемлемым. - person Hulk; 17.06.2020

Анонимный класс никогда не бывает final (§8.1.1.2).

JLS 11–15.9 .5. Объявления анонимных классов

Я не знал причины этого, но, согласно ответу @Hulk и этому отчет об ошибке, кажется, спецификация предыдущих версий немного ввела нас в заблуждение, говоря, что анонимные классы являются окончательными.

person Andrew Tobilko    schedule 03.01.2019
comment
Вы ссылаетесь на тот же раздел, что и OP, но на более свежую версию JLS. Возможно, стоит описать, почему существует несоответствие между двумя версиями, а не просто цитировать более новую. - person Michael; 03.01.2019
comment
@GhostCat Я обычно комментирую повторное открытие, это плохо, что я не дал вам знать. Я ленивый*, и, наверное, надо было добавить больше ссылок, а не открывать заново :) - person Andrew Tobilko; 11.06.2019
comment
@AndrewTobilko Неважно, как часто Сотириос хорошо справлялся с закрытием дубликатов ;-) - person GhostCat; 11.06.2019

Анонимные классы неявно считаются final, поскольку вы не можете создавать из них подклассы. Это не означает, что модификатор Modifier.FINAL должен быть установлен для анонимных классов.

person Eran    schedule 03.01.2019
comment
Когда вы говорите неявно final, не означает ли это, что он FINAL и должен следовать всем правилам ключевого слова Final в java? - person Sachin Sachdeva; 03.01.2019
comment
@ShowStopper хорошо, он действительно следует правилам final: вы не можете подклассифицировать их, потому что нет возможности ссылаться на них как на тип. Я полагаю, именно поэтому они неявно окончательные. - person Moira; 03.01.2019

См. Javadoc от Class.getModifiers(): https://docs.oracle.com/javase/10/docs/api/java/lang/Class.html#getModifiers()

Он говорит: «... Значения других его модификаторов не определяются этой спецификацией».

person Prasad Karunagoda    schedule 03.01.2019