Что такое синтетический метод?
Начиная с _1 _ класс (и его родительский элемент, 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. Измените разрешения
Поля Package-private или 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
.
person
dimo414
schedule
26.04.2013