2017-09-11 1 views
0

Es ist mir gerade in den Sinn gekommen, dass es mit Lambdas möglich sein könnte, etwas wie SwingBuilder in nativem Java zu erstellen - aber es scheint, als ob es jetzt möglich wäre, wenn das möglich wäre.Ist es möglich, Java Lambdas zu verwenden, um so etwas wie Groovys SwingBuilder zu implementieren?

Gibt es einen Grund, warum dies nicht möglich war, oder wurde es durchgeführt?

Ich weiß, das ist ähnlich wie SwingBuilder like GUI syntax for Java?, aber ich hatte gehofft, Lambdas in den Mix hinzuzufügen. SwingBuilder basiert hauptsächlich auf Lambdas, das war vor Java 8 absolut nicht möglich. Es verwendet auch ein bisschen dynamische Programmierung und das kann natürlich nicht verwendet werden, so dass es niemals so sauber sein wird, aber ich denke, es ist möglich ...

+0

Sollte es möglich sein, in die Nähe zu kommen, werden Sie nicht die Nettigkeit des missingProperty Zeugs bekommen, um eine Kontrolle über den Namen zu bekommen ... Ich würde jedoch JavaFX als Ziel haben, da Swing auf dem Weg zur Tür ist –

Antwort

1

Zuerst müssen wir identifizieren, welche Probleme wir lösen wollen. Das größte Problem ist die Erstellung einfacher Event-Bindings, die in früheren Java-Versionen die Verwendung von inneren Klassen benötigten. Dies kann in den meisten Fällen durch einen Lambda-Ausdruck für Single-Method-Listener-Interfaces ersetzt werden, für Multi-Method-Interfaces benötigen Sie Adapter, wie in this oder that Q & A beschrieben, aber dies muss nur einmal gemacht werden.

Das andere Problem ist die Struktur des Initialisierungscodes. Prinzipiell funktioniert imperativer Code gut. Wenn Sie jedoch einige Eigenschaften einer Komponente ändern möchten, die Sie einem Container hinzufügen möchten, müssen Sie container.add(new ComponentType()); ändern, um eine neue lokale Variable einzufügen, die von den nachfolgenden Anweisungen verwendet werden kann. Das Hinzufügen zu einem Container selbst erfordert auch das Speichern in einer Variablen. Während Java es erlaubt, den Geltungsbereich lokaler Variablen durch geschweifte Klammern zu begrenzen, ist das Ergebnis immer noch schwerfällig.

Dies ist der beste Ausgangspunkt. Wenn wir z.B.

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; 
    } 
} 

Diese einfachen generischen Methoden sind bereits leise, weil sie miteinander kombiniert werden können. Mit import static kann eine Verwendung Website aussehen

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); 

Im Vergleich zu Groovy, müssen wir noch eine Variable verwenden, das Ziel des Verfahrens Anrufungen auszudrücken Eigenschaften zu ändern, aber diese Variable kann so einfach wie name -> deklariert werden Typ verwendet Rückschluss bei der Implementierung der Consumer über Lambda-Ausdruck. Außerdem ist der Gültigkeitsbereich der Variablen automatisch auf die Dauer der Initialisierung beschränkt.

Mit diesem Startpunkt können Sie spezielle Methoden für häufig verwendete Komponenten und/oder häufig verwendete Eigenschaften sowie die bereits erwähnten Factory-Methoden für Listener mit mehreren Methoden hinzufügen. Z.B.

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; 
} 

Aber ich denke, das Bild ist klar.

Verwandte Themen