Kommunikation zwischen 2 Klassen

Carcharoth

Bekanntes Mitglied
Guten Abend

Ich habe eine kleine GUI programmiert die einen JTree beinhaltet (in Form einer Klasse TreePanel) und ein JTextField.
Bei Anklicken eines Elementes im Tree soll der JTextField mit dem Namen des angeklickten Elementes angepasst werden.

Um das JTextField aus der Klasse TreePanel heraus updaten zu können, habe ich in der Klasse UF eine public static Methode gemacht.
Ist das der korrekte Weg oder wie mache ich das besser ?

Danke und Gruss


Java:
public class UF extends JFrame {
    private Container contentPane;
    public static JTextField textFieldHersteller = new JTextField();
    
    public UF() {
        setSize(800, 600);
        setLocationRelativeTo(null);
        contentPane = getContentPane();
        contentPane.setLayout(null);
        contentPane.add(new TreePanel(50,50,300,400));
        textFieldHersteller.setBounds(500, 80,200,30);
        contentPane.add(textFieldHersteller);
    }

    public static void updateTextField(String hersteller) {
        textFieldHersteller.setText(hersteller);
    }
    
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    UF frame = new UF();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
}


Java:
public class TreePanel extends JPanel implements TreeSelectionListener {
    public JTree tree;

    public TreePanel(int x, int y, int width, int height) {
        DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root");
        root.add(new DefaultMutableTreeNode("Child 1"));
        root.add(new DefaultMutableTreeNode("Child 2"));
        
        tree = new JTree(root);
        tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
        tree.addTreeSelectionListener(this);

        this.setBounds(x,y,width, height);
        this.setLayout(null);
        JScrollPane scrollPane = new JScrollPane(tree);
        scrollPane.setSize(new Dimension(width, height));
        add(scrollPane);
    }

    public void valueChanged(TreeSelectionEvent e) {
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();

        if (node != null) {
            Object nodeInfo = node.getUserObject();
            if (node.isLeaf()) {
                UF.updateTextField(nodeInfo.toString());
            }
        }
    }
}
 
Beste Antwort
Ist das der korrekte Weg
Für den Anfang merkst Du Dir am besten, dass static nur für Konstanten und main verwendet wird. Das ist natürlich sehr verkürzt, aber gerade am Anfang solltest Du Dich mit dem Gedanken anfreunden. Mit static deklarierst Du Klassenvariablen und -methoden, also Variablen und Methoden, die im Kontext der Klasse existieren und somit ohne Objekt auskommen. Das ist nun gerade nicht objekt-orientiert.

In Deinem Code existiert textFieldHersteller also im Kontext der Klasse. Für den Zugriff auf das Feld benötigst Du kein Objekt, d. h. im Umkehrschluss, dass sich alle Instanzen von UF dieses Feld teilen -> nix gut.

Wie funktioniert es dann? In der Objektorientierung willst Du mit Objekten arbeiten, daher ist die...
Ist das der korrekte Weg
Für den Anfang merkst Du Dir am besten, dass static nur für Konstanten und main verwendet wird. Das ist natürlich sehr verkürzt, aber gerade am Anfang solltest Du Dich mit dem Gedanken anfreunden. Mit static deklarierst Du Klassenvariablen und -methoden, also Variablen und Methoden, die im Kontext der Klasse existieren und somit ohne Objekt auskommen. Das ist nun gerade nicht objekt-orientiert.

In Deinem Code existiert textFieldHersteller also im Kontext der Klasse. Für den Zugriff auf das Feld benötigst Du kein Objekt, d. h. im Umkehrschluss, dass sich alle Instanzen von UF dieses Feld teilen -> nix gut.

Wie funktioniert es dann? In der Objektorientierung willst Du mit Objekten arbeiten, daher ist die Antwort recht einfach: Du brauchst ein Objekt (genauer: eine Referenz auf ein Objekt).

Im einfachsten Fall übergibst Du also z. B. Deinem TreePanel im Konstruktor noch ein UF-Objekt, dann kannst Du die Methode updateTextField (die natürlich nicht mehr static ist) über dieses Objekt aufrufen.
 
Beste Antwort
Danke für die sehr klare Erläuterung.
Ich habe es nun umgesetzt (funktioniert auch). Ist aber das UF-Objekt korrekt dem Konstruktor von TreePanel übergeben? Ich frage deshalb, weil in anderen Sprachen die ich kenne ein Parameter einer Methode byRef oder byVal übergeben werden kann (also entweder gleich das Objekt oder eine Referenz darauf).

Java:
public class UF extends JFrame {
    private Container contentPane;
    public static JTextField textFieldHersteller = new JTextField();
    
    public UF() {
        setSize(800, 600);
        setLocationRelativeTo(null);
        contentPane = getContentPane();
        contentPane.setLayout(null);
        contentPane.add(new TreePanel(50,50,300,400, this));
        textFieldHersteller.setBounds(500, 80,200,30);
        contentPane.add(textFieldHersteller);
    }

    public void updateTextField(String hersteller) {
        textFieldHersteller.setText(hersteller);
    }
    
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    UF frame = new UF();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

Java:
public class TreePanel extends JPanel implements TreeSelectionListener {
    public JTree tree;
    private UF uf;

    public TreePanel(int x, int y, int width, int height, UF uf) {
        this.uf = uf;
        
        DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root");
        root.add(new DefaultMutableTreeNode("Child 1"));
        root.add(new DefaultMutableTreeNode("Child 2"));
        
        tree = new JTree(root);
        tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
        tree.addTreeSelectionListener(this);

        this.setBounds(x,y,width, height);
        this.setLayout(null);
        JScrollPane scrollPane = new JScrollPane(tree);
        scrollPane.setSize(new Dimension(width, height));
        add(scrollPane);
    }

    public void valueChanged(TreeSelectionEvent e) {
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();

        if (node != null) {
            Object nodeInfo = node.getUserObject();
            if (node.isLeaf()) {
                uf.updateTextField(nodeInfo.toString());
            }
        }
    }
}
 
Java kennt nur call by Value. Aber bei Instanzen hat man immer eine Referenz und die wird hier korrekt übergeben.
Eine Referenz ist in etwa vergleichbar mit einem Pointer in C oder C++. Das, worauf verwiesen wird, kannst du ändern.
 
OK, Danke.

By the way. Ich bin mir nicht sicher, ob ich hier korrekt mit dem Threading in Bezug auf Swing umgehe. Ich denke zwar schon, weil alles rund um die GUI via Main dem Aufruf in der Main Methode innerhalb des EDT stattfindet.

Was wäre wenn ich im Konstruktor UF noch eine aufwändige Arbeit (z.B. ein Datenbank Query) hätte ? wie könnte ich den ausserhalb des EDT ausführen ?

Java:
public class UF extends JFrame {
    private Container contentPane;

    public UF() {
        query(); //aufwändige Aufgabe die ausserhalb des EDT stattfinden sollte
        
        setLocationRelativeTo(null);
        contentPane = getContentPane();
        contentPane.setLayout(null);
        contentPane.add(new TreePanel(50,50,300,400, this));
        textFieldHersteller.setBounds(500, 80,200,30);
        contentPane.add(textFieldHersteller);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    UF frame = new UF();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
}
 
Was wäre wenn ich im Konstruktor UF noch eine aufwändige Arbeit (z.B. ein Datenbank Query) hätte ? wie könnte ich den ausserhalb des EDT ausführen ?
Das könnte man einfach über einen Thread machen. (Klasse Thread)

Wichtig ist dabei, dass Änderungen an der UI immer im EDT stattfinden müssen. Das geht z.B. über SwingUtilities invokeLater oder invokeAndWait.

Es gibt aber auch in Swing spezielle Klassen, die einem das abnehmen. SwingWorker z.B. - das bietet dann eben diese Aktualisierungen direkt im EDT zu machen.
 
Und da der Hinweis auf Lambda Expressions und Methodenreferenzen. Seid Java 8 schreibt man das halt nicht mehr so.

new Thread( () -> query() ).start();
new Thread(this::query).start();

Das ist doch deutlich kürzer und lesbarer, oder?
 

Neue Themen


Zurück
Oben