Что такое "синтетический" метод?
Начиная с класса Method
(и его родителя, Member
) мы узнаем, что синтетические члены" введены компилятором ", и что JLS §13.1 расскажет нам больше.Он отмечает:
Конструкция, испускаемая компилятором Java, должна быть помечена как синтетическая , если она не соответствует конструкции, объявленной явно или неявно в исходном коде
Поскольку в этом разделе обсуждается двоичная совместимость, на JVMS также стоит сослаться, и JVMS §4.7.8 добавляет немного больше контекста:
Член класса, который делаетне появляется в исходном коде и должен быть помечен с помощью атрибута Synthetic
, иначе должен быть установлен флаг ACC_SYNTHETIC
.Единственными исключениями из этого требования являются методы, сгенерированные компилятором, которые не считаются артефактами реализации ....
Атрибут Synthetic
был введен в JDK 1.1 для поддержки вложенных классов и интерфейсов.
Другими словами, «синтетические» методы - это артефакт реализации, который Java-компилятор вводит для поддержки языковых функций, которые сама JVM не поддерживает.
В чем проблема?
Вы столкнулись с одним из таких случаев;Вы пытаетесь получить доступ к полю private
класса из анонимного внутреннего класса.Язык Java разрешает это, но JVM не поддерживает его, поэтому компилятор Java генерирует синтетический метод, который предоставляет поле private
для внутреннего класса.Это безопасно, потому что компилятор не позволяет никаким другим классам вызывать этот метод, однако он создает две (маленькие) проблемы:
- Объявляются дополнительные методы.Это не должно быть проблемой для подавляющего большинства случаев использования, но если вы работаете в ограниченной среде, такой как Android, и генерируют множество таких синтетических методов, у вас могут возникнуть проблемы.
- Доступ к этому полю осуществляется косвенным путем, а не напрямую.Это тоже не должно быть проблемой, за исключением случаев использования с высокой чувствительностью к производительности.Если вы не хотите использовать метод получения здесь из соображений производительности, вам не нужен синтетический метод получения.На практике это редко является проблемой.
Короче говоря, они действительно неплохие.Если у вас нет конкретной причины избегать синтетических методов (то есть вы окончательно определили, что они являются узким местом в вашем приложении), вам следует просто позволить компилятору сгенерировать их так, как они считают нужным.Попробуйте отключить предупреждение Eclipse, если оно вас беспокоит.
Что мне с ними делать?
Если вы действительно хотите запретить компилятору генерировать синтетические методы, у вас есть несколько вариантов:
Опция 1: Изменить права доступа
Поля пакета или protected
доступны непосредственно для внутренних классов.Особенно для чего-то вроде приложения Swing это должно быть хорошо.Но вы говорите, что хотите избежать этого, и мы продолжаем.
Вариант 2: Создайте получатель
Оставьте ваши поля в покое, но явно создайте получатель protected
или public
,и используйте это вместо этого.По сути, это то, что компилятор делает для вас автоматически, но теперь у вас есть прямой контроль над поведением метода.
Вариант 3: Используйте локальную переменную и делитесь ссылкой с обоими классами
Это больше кода, но это мой личный фаворит, поскольку вы делаете явные отношения между внутренним и внешним классом.
public Synthetic() {
// Create final local instance - will be reachable by the inner class
final JButton testButton = new JButton("Run");
testButton.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent ae) {
/* Sample code */
if( testButton.getText().equals("Pause") ) {
resetButton();
} else if( testButton.getText().equals("Run") ) {
testButton.setText("Pause");
}
}
});
// associate same instance with outer class - this.testButton can be final too
this.testButton = testButton;
}
На самом деле это не всегда то, что вы хотите сделать.Например, если testButton
может измениться, чтобы указать на другой объект позже, вам нужно будет перестроить ваш ActionListener
снова (хотя это также приятно более явно, так что, возможно, это особенность), но я считаю, что это вариант, которыйнаиболее наглядно демонстрирует свое намерение.
Помимо безопасности потоков
Ваш пример Test
класс не является потокобезопасным - testString
устанавливается в отдельном Thread
, но вы не синхронизируете это назначение.Пометка testString
как volatile
будет достаточной для того, чтобы все потоки увидели обновление.В примере Synthetic
такой проблемы нет, поскольку testButton
задается только в конструкторе, но в этом случае рекомендуется пометить testButton
как final
.