Я унаследовал большое приложение Swing.Рассмотрим версию этого главного окна графического интерфейса этого класса:
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
final class ListenerExample1 extends JFrame implements ActionListener {
private final JButton button = new JButton("Click me");
private int clickCount = 0;
public static void main(String... args) throws Exception {
SwingUtilities.invokeAndWait( () -> {
ListenerExample1 instance = new ListenerExample1();
instance.setVisible(true);
instance.pack();
} );
}
ListenerExample1() throws IllegalStateException {
if (!EventQueue.isDispatchThread()) throw new IllegalStateException("the current thread (" + Thread.currentThread().toString() + ") is not EventQueue's dispatch thread");
add(button);
button.addActionListener(this);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
}
@Override public void actionPerformed(ActionEvent e) throws IllegalStateException {
if (!EventQueue.isDispatchThread()) throw new IllegalStateException("the current thread (" + Thread.currentThread().toString() + ") is not EventQueue's dispatch thread");
markButtonAsClicked();
}
void markButtonAsClicked() throws IllegalStateException {
if (!EventQueue.isDispatchThread()) throw new IllegalStateException("the current thread (" + Thread.currentThread().toString() + ") is not EventQueue's dispatch thread");
button.setText("click #" + (++clickCount));
}
}
Этот код сокращен в 100 раз по сравнению с фактическим приложением, чтобы получить Минимальный, Полный и Проверяемый пример .
IntelliJ правильно предупреждает меня, что эта строка
button.addActionListener(this);
приводит к тому, что эта ссылка исчезает во время строительства.
Я полностью осознаю, почему это обычно плохо.Это подробно обсуждается в "Библии", Параллелизм Java на практике .Некоторые специальные статьи Гетца на эту тему: Техника безопасного строительства и Быть хорошим слушателем событий .
Однако действительно ли это плохо в этом?case?
В частности, обратите внимание, что этот класс должен быть затронут только одним потоком.Это из-за правила, согласно которому компоненты Swing должны быть затронуты только потоком диспетчеризации EventQueue («EDT»).Итак, основной метод использует SwingUtilities.invokeAndWait, а все методы конструктора и экземпляра проверяют, является ли вызывающий поток EDT.
Также обратите внимание, что этот класс является окончательным, что исключает проблемы подкласса.
Итак, я думаю, что приведенный выше код действительно безопасен, несмотря на предупреждение IntelliJ, но я хочу знать, пропустил ли я какую-то тонкую проблему.
Есть причины, которые не очевидныв приведенном выше упрощенном коде, почему я хочу добавить это в качестве прослушивателя в несколько конструкторов моего реального приложения.
Говоря о IntelliJ, я отмечаю, что приведенный выше код можно упростить.Вместо того, чтобы класс реализовывал ActionListener, который, следовательно, без веских причин предоставляет метод actionPerformed, вы можете использовать лямбду для неявного создания внутреннего класса экземпляра:
import java.awt.EventQueue;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
final class ListenerExample2 extends JFrame {
private final JButton button = new JButton("Click me");
private int clickCount = 0;
public static void main(String... args) throws Exception {
SwingUtilities.invokeAndWait( () -> {
ListenerExample2 instance = new ListenerExample2();
instance.setVisible(true);
instance.pack();
} );
}
ListenerExample2() throws IllegalStateException {
if (!EventQueue.isDispatchThread()) throw new IllegalStateException("the current thread (" + Thread.currentThread().toString() + ") is not EventQueue's dispatch thread");
add(button);
button.addActionListener( (e) -> markButtonAsClicked() );
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
}
void markButtonAsClicked() throws IllegalStateException {
if (!EventQueue.isDispatchThread()) throw new IllegalStateException("the current thread (" + Thread.currentThread().toString() + ") is not EventQueue's dispatch thread");
button.setText("click #" + (++clickCount));
}
}
К моему удивлению, IntelliJ не предупреждает, чтоэта новая лямбда-линия
button.addActionListener( (e) -> markButtonAsClicked() );
снова вызывает эту ссылку во время строительства - и эта строка наверняка пропускает эту ссылку!
Это простодефект в коде проверки IntelliJ?
Или есть какая-то тонкая причина, почему лямбда-код на самом деле безопасен в целом?(Этот новый лямбда-код должен быть безопасным в данном конкретном случае по тем же причинам, которые обсуждались выше для исходной версии.)