[JAVA] Stoppen Sie die Tasten vom Typ String und führen Sie die Markierungsschnittstelle ein

Einführung

In Java-Bibliotheken werden häufig Zeichenfolgen von Typobjekten angezeigt, die als ** Werte ** (im Folgenden als "Schlüssel" bezeichnet) verwendet werden, um Objekte wie Schlüssel und Eigenschaftsnamen zu identifizieren. Beispielsweise die Klasse "java.util.Properties", mit der die Eigenschaftsliste eingeführt wird, die Klasse "java.beans.PropertyChangeSupport", die zum Senden von Ereignissen wie Benachrichtigungen über Eigenschaftsänderungen usw. verwendet wird. Es wird gesagt, dass es abnimmt und immer mit JavaFx verglichen wird.

Wenn der Wert in der Methode mit dem Typ String einen Tippfehler enthält ** oder der Schlüsselname aufgrund einer Änderung in der Implementierung ** geändert wird, kann das Objekt nicht korrekt referenziert werden und es tritt ein Problem auf. Du darfst.

Beispiel für einen Fehler bei der Werterfassung aufgrund eines Tippfehlers im Schlüsselnamen


Map<String, String> entries = new HashMap<>() {{
  put("hoge", "Hoge");
  put("huga", "Fuga");
  put("piyo", "Piyo");
}};
System.out.println(entries.get("hoge")); //Ausgabe: Hoge
System.out.println(entries.get("hige")); //Ausgabe: null (Erfassung fehlgeschlagen)

Beispiel für einen Fehler bei der Werterfassung aufgrund einer Implementierungsänderung


Map<String, String> entries = new HashMap<>() {{
  put("foo", "Huh");  //Implementierungsänderungen
  put("huga", "Fuga");
  put("bar", "Bah");  //Implementierungsänderungen
}};
System.out.println(entries.get("hoge")); //Ausgabe: null (Erfassung fehlgeschlagen)
System.out.println(entries.get("huga")); //Ausgabe: Fuga

Eine solche Gefahr kann durch Einführung der Markierungsschnittstelle [^ Markierungsschnittstelle] beseitigt werden. Dieses Mal möchte ich anhand der zuvor eingeführten CardLayout-Klasse als Beispiel erläutern.

Vor der Einführung

Betrachten Sie als nächstes den Bildschirm, der durch den reduzierten Code A angelegt ist.

Code A

python


public class App {

    public static void main(String[] args) {
        new JFrame("Sample") {{
            setSize(350, 300);
            setLocationRelativeTo(null);
            setDefaultCloseOperation(EXIT_ON_CLOSE);
            setVisible(true);
            //CardLayout-Generierung
            CardLayout cards = new CardLayout();
            //Rahmenmitte
            JLabel[] labels = {
                new JLabel("Card 1"),
                new JLabel("Card 2"),
                new JLabel("Card 3"),
                new JLabel("Card 4")
            };
            Font fnt = new Font("Meilio", Font.PLAIN, 40);
            JPanel center = new JPanel();
            center.setLayout(cards);
            for (int i = 0; i < labels.length; i++) {
                labels[i].setFont(fnt);
                labels[i].setVerticalAlignment(JLabel.CENTER);
                labels[i].setHorizontalAlignment(JLabel.CENTER);
                center.add(labels[i], "Card" + (i + 1));
            }
            //Rahmen unten
            JButton[] buttons = {
                new JButton("Card 1"),
                new JButton("Card 2"),
                new JButton("Card 3"),
                new JButton("Card 4")
            };
            JPanel south = new JPanel();
            for (int i = 0; i < buttons.length; i++) {
                int l_i = i;
                buttons[i].addActionListener(e -> cards.show(center, "Card" + (l_i + 1)));
                south.add(buttons[i]);
            }
            //Hinzufügen eines Panels zum Rahmen
            add(center, BorderLayout.CENTER);
            add(south, BorderLayout.SOUTH);
        }};
    }
}

Durch Ausführen dieses Codes und Klicken auf jede Schaltfläche können Sie wie unten gezeigt zwischen den vier Beschriftungen wechseln.

ok.gif

Wo es in Code A ist

python


buttons[i].addActionListener(e -> cards.show(center, "Card" + (l_i + 1)));

In dem Moment, in dem Sie die "Karte" mit "Cerd" verwechseln, funktioniert die Umschaltung wie folgt nicht richtig.

ng.gif

Dies liegt daran, dass die show-Methode nicht mehr auf jede Beschriftung verweisen kann, die dem Bedienfeld in der Mitte des Rahmens durch die add-Methode zugeordnet ist, die die Bezeichner der vier Beschriftungen im zweiten Argument angibt.

python


center.add(labels[i], "Card" + (i + 1));

buttons[i].addActionListener(e -> cards.show(center, "Cerd" + (l_i + 1))); // "Card"nicht!

Es verursacht keinen Kompilierungsfehler. Wenn Sie ihn also nicht bemerken oder jemanden bitten, es Ihnen zu sagen, ist es in Ordnung, wenn Sie die Ursache des Problems nicht kennen.

Einführung

Also werden wir eine Marker-Schnittstelle einführen.

Es ist nicht sinnvoll, nur eine Markierungsschnittstelle einzuführen. Bereiten Sie also eine Aufzählung vor, definieren Sie die Schlüssel und lassen Sie diese Aufzählung die Markierungsschnittstelle implementieren.

python


//Marker-Schnittstelle
interface CardKey {}
//Enumerator, der die Markierungsschnittstelle implementiert
enum Card implements CardKey {
    CARD1, CARD2, CARD3, CARD4;
}

Da der zuvor angezeigte hinzugefügte Schlüssel und die Show-Methode den definierten Schlüssel nicht als Argument angeben können, definieren Sie die nächste reduzierte Dienstprogrammklasse, damit die Komponente, die durch Umschalten mit dem Schlüssel angezeigt werden soll, zugeordnet werden kann. ..

Dienstprogrammklasse

python


class CardLayoutUtil {
    private CardLayout cards = new CardLayout();
    private JPanel panel = new JPanel();

    private CardLayoutUtil(JPanel panel) {
        this.panel = panel;
        cards = (CardLayout) panel.getLayout();
    }
    
    /**
     *Erstellen Sie eine Instanz dieser Klasse basierend auf dem Panel, das als Argument verwendet wird.
     *
     * @param panel Mit der Instanz verknüpftes Panel
     * @Rückgabe Instanz dieser Klasse
     * @löst NullPointerException aus Wenn das Argument null ist
     * @löst eine IllegalArgumentException aus, wenn CardLayout im Panel für das Argument nicht festgelegt ist
     */
    static CardLayoutUtil of(JPanel panel) {
        Objects.requireNonNull(panel);
        if (!(panel.getLayout() instanceof CardLayout))
            throw new IllegalArgumentException("CardLayout ist nicht in dem Bereich festgelegt, in dem das Argument verwendet wird.");
        return new CardLayoutUtil(panel);
    }

    /**
     *Registrieren Sie die Komponente in dem Bereich, der der Instanz zugeordnet ist.
     *
     * @param comp Zu registrierende Komponente
     * @param key Der der Komponente zugeordnete Schlüssel
     * @löst NullPointerException aus Wenn ein Argument null ist
     */
    void addCard(Component comp, CardKey key) {
        Objects.requireNonNull(comp);
        Objects.requireNonNull(key);
        panel.add(comp, key.toString());
    }

    /**
     *Zeigen Sie die Komponente an, die dem als Argument verwendeten Schlüssel zugeordnet ist.
     *
     * @param key Der der Komponente zugeordnete Schlüssel
     * @löst NullPointerException aus Wenn das Argument null ist
     */
    void showCard(CardKey key) {
        Objects.requireNonNull(key);
        cards.show(panel, key.toString());
    }
}

Da der Layout-Manager des der Dienstprogrammklasse zugeordneten Panels nicht CardLayout ist und das erwartete Verhalten für die als Argumente jeder Methode verwendeten Schlüssel und Komponenten null sein kann, kann keine Ausnahmebehandlung durchgeführt werden.

Erstellen Sie ein Objekt dieser Dienstprogrammklasse und bereiten Sie für jeden Schlüssel ein Array vor, damit Sie es in einer Schleife drehen können.

python


//Erstellen eines CardLayoutUtil-Objekts
CardLayoutUtil util = CardLayoutUtil.of(center);
Card[] cardKeys = Card.class.getEnumConstants();

Der Rest ist in Code A.

python


center.add(labels[i], "Card" + (i + 1));

buttons[i].addActionListener(e -> cards.show(center, "Cerd" + (l_i + 1))); // "Card"nicht!

Teil von

python


util.addCard(labels[i], cardKeys[i]);

buttons[i].addActionListener(e -> util.showCard(cardKeys[l_i]));

Wenn Sie es durch ersetzen, müssen Sie sich keine Gedanken über die oben genannten Probleme machen, die durch Tippfehler oder Änderungen in der Implementierung verursacht werden.

wichtiger Punkt

Diese Methode wird unter Verwendung der Zeichenfolgendarstellung des Schlüssels (Enumerators) realisiert, der durch "toString ()" erhalten wird. Wenn ** derselbe Schlüssel einer anderen Komponente zugeordnet und wiederverwendet wird ** oder ** ein Enumerator mit demselben Namen zwischen verschiedenen Enumeratoren ** definiert ist, funktioniert dies nicht gut [^] toString].

Ein Beispiel für die Definition eines gleichnamigen Enumerators zwischen Enumeratoren


enum CardsA implements CardKey {
    CARD1, CARD2, CARD3, CARD4;
}
enum CardsB implements CardKey {
    CARD1, CARD2, CARD3, CARD4;
}
Codebeispiel, das nicht richtig funktioniert

python


//Rahmenmitte
JLabel[] labelsA = {
    new JLabel("Card 1"),
    new JLabel("Card 2"),
    new JLabel("Card 3"),
    new JLabel("Card 4")
};
JLabel[] labelsB = {
    new JLabel("Card 5"),
    new JLabel("Card 6"),
    new JLabel("Card 7"),
    new JLabel("Card 8")
};
... //Kürzung
//Erstellen eines CardLayoutUtil-Objekts
CardLayoutUtil util = CardLayoutUtil.of(center);
CardsA[] cardKeysA = CardsA.class.getEnumConstants();
CardsB[] cardKeysB = CardsB.class.getEnumConstants();
for (int i = 0; i < labelsA.length; i++) {
    ... //Kürzung
    util.addCard(labelsA[i], cardKeysA[i]);
}
for (int i = 0; i < labelsB.length; i++) {
    ... //Kürzung
    util.addCard(labelsB[i], cardKeysB[i]);
}
//Rahmen unten
JButton[] buttonsA = {
    new JButton("Card 1"),
    new JButton("Card 2"),
    new JButton("Card 3"),
    new JButton("Card 4")
};
JButton[] buttonsB = {
    new JButton("Card 5"),
    new JButton("Card 6"),
    new JButton("Card 7"),
    new JButton("Card 8")
};
JPanel south = new JPanel();
for (int i = 0; i < buttonsA.length; i++) {
    ... //Kürzung
    south.add(buttonsA[i]);
}
for (int i = 0; i < buttonsB.length; i++) {
    ... //Kürzung
    south.add(buttonsB[i]);
}

ng2.gif

Da dies jedoch ein Phänomen ist, das auftritt, weil der Schlüssel nur durch den Enumeratornamen identifiziert wird, befindet er sich in der Utility-Klasse.

python


cards.show(panel, key.toString());

panel.add(comp, key.toString());

Teil von

python


cards.show(panel, key.getClass().getName() + "." + key.toString());

panel.add(comp, key.getClass().getName() + "." + key.toString());

Durch Umschreiben als wird die Schlüsselidentifikation mit ** vollständig qualifiziertem Klassennamen [^ vollständig qualifizierter Klassenname] + Enumeratorname ** der Aufzählung durchgeführt und funktioniert normal.

ok2.gif

Während der Schleife wird außerdem eine ArrayIndexOutOfBoundsException ausgelöst, wenn die Anzahl der Schlüssel und Komponenten nicht übereinstimmt. Dieser Bereich kann verbessert werden, indem die Utility-Klasse etwas chaotischer gestaltet wird.

[^ Marker-Schnittstelle]: Eine Marker-Schnittstelle ist eine Schnittstelle, die nichts im Inneren implementiert. Es wird häufig verwendet, um der zu implementierenden Klasse eine Bedeutung zu geben.

[^ toString]: Auch wenn Sie toString () überschreiben, damit alle gleichen Zeichenfolgendarstellungen zurückgegeben werden, ist dies nutzlos, da der Schlüssel nicht bestimmt werden kann.

[^ Vollqualifizierter Klassenname]: Der Klassenname, der den in der Importanweisung verwendeten Paketnamen enthält.

Recommended Posts

Stoppen Sie die Tasten vom Typ String und führen Sie die Markierungsschnittstelle ein
Schnittstelle und Zusammenfassung
Vererbung und Schnittstelle.