Можно ли использовать Java Lambdas для реализации чего-то вроде Groovy SwingBuilder?

Мне только что пришло в голову, что с помощью Lambdas можно было бы создать что-то вроде SwingBuilder на родной Java — но кажется, что если бы это было возможно, это было бы уже сделано.

Есть ли какая-то причина, по которой это нельзя было сделать или это уже было сделано?

Я понимаю, что это похоже на SwingBuilder как синтаксис GUI для Java?, но я надеялся добавить Lambdas в смесь. SwingBuilder в основном построен на Lambdas, это было абсолютно невозможно до Java 8. Он также использует немного динамического программирования, и это, очевидно, нельзя использовать, поэтому он никогда не будет чистым AS, но я думаю, что это возможно...


person Bill K    schedule 11.09.2017    source источник
comment
Должна быть возможность приблизиться, вы не получите приятности материала MissingProperty, чтобы получить элемент управления по имени, хотя... Я бы нацелился на JavaFX, поскольку Swing уже на пути к двери   -  person tim_yates    schedule 12.09.2017


Ответы (1)


Во-первых, мы должны определить, какие проблемы мы хотим решить. Самой большой проблемой является создание простых привязок событий, которые требовали использования внутренних классов в предыдущих версиях Java. В большинстве случаев это можно заменить лямбда-выражением для интерфейсов прослушивателя с одним методом, для интерфейсов с несколькими методами вам понадобятся адаптеры, как описано в это или это вопросы и ответы, но это нужно сделать только один раз.

Другой проблемой является структура кода инициализации. В принципе, императивный код работает нормально, но если вы хотите изменить некоторые свойства компонента, который собираетесь добавить в контейнер, вам нужно изменить container.add(new ComponentType());, чтобы ввести новую локальную переменную, которая будет использоваться последующими операторами. Кроме того, добавление в сам контейнер требует сохранения его в переменной. Хотя Java позволяет ограничивать область действия локальных переменных фигурными скобками, результат все равно получается неуклюжим.

Это лучшая отправная точка. Если мы используем, например.

public class SwingBuilder {
    public static <T> T build(T instance, Consumer<T> prepare) {
        prepare.accept(instance);
        return instance;
    }
    public static <T extends Container> T build(
                                        T instance, Consumer<T> prepare, Component... ch) {
        return build(build(instance, prepare), ch);
    }
    public static <T extends Container> T build(T instance, Component... ch) {
        for(Component c: ch) instance.add(c);
        return instance;
    }
}

Эти простые универсальные методы уже достаточно эффективны благодаря тому, что их можно комбинировать. Используя import static, сайт использования может выглядеть так

JFrame frame = build(new JFrame("Example"),
    f -> {
        f.getContentPane().setLayout(
            new BoxLayout(f.getContentPane(), BoxLayout.LINE_AXIS));
        f.setResizable(false);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    },
    build(new JLabel("\u263A"), l -> l.setFont(l.getFont().deriveFont(36f))),
    Box.createHorizontalStrut(16),
    build(new JPanel(new GridLayout(0, 1, 0, 5)),
        new JLabel("Hello World"),
        build(new JButton("Close"), b -> {
            b.addActionListener(ev -> System.exit(0));
        })
    )
);
frame.pack();
frame.setVisible(true);

По сравнению с Groovy нам по-прежнему приходится использовать переменную для выражения цели вызова метода для изменения свойств, но эту переменную можно объявить так же просто, как name ->, используя вывод типа при реализации Consumer через лямбда-выражение. Кроме того, область действия переменной автоматически ограничивается длительностью инициализации.

Используя эту отправную точку, вы можете добавить специализированный метод для часто используемых компонентов и/или часто используемых свойств, а также уже упомянутые фабричные методы для слушателей с несколькими методами. Например.

public static JFrame frame(String title, Consumer<WindowEvent> closingAction,
                           Consumer<? super JFrame> prepare, Component... contents) {
    JFrame f = new JFrame(title);
    if(closingAction!=null) f.addWindowListener(new WindowAdapter() {
        @Override public void windowClosing(WindowEvent e) {
            closingAction.accept(e);
        }
    });
    if(prepare!=null) prepare.accept(f);
    final Container target = f.getContentPane();
    if(contents.length==1) target.add(contents[0], BorderLayout.CENTER);
    else {
        target.setLayout(new BoxLayout(target, BoxLayout.PAGE_AXIS));
        for(Component c: contents) target.add(c);
    }
    return f;
}

Но думаю, картина ясна.

person Holger    schedule 12.09.2017