Frage

Ist es völlig gegen die Java-Methode, strukturähnliche Objekte zu erstellen?

class SomeData1 {
    public int x;
    public int y;
}

Ich kann mir eine Klasse mit Accessoren und Mutatoren vorstellen, die eher Java ähneln.

class SomeData2 {
    int getX();
    void setX(int x);

    int getY();
    void setY(int y);

    private int x;
    private int y;
}

Die Klasse aus dem ersten Beispiel ist notational praktisch.

// a function in a class
public int f(SomeData1 d) {
    return (3 * d.x) / d.y;
}

Das ist nicht so bequem.

// a function in a class
public int f(SomeData2 d) {
    return (3 * d.getX()) / d.getY();
}
War es hilfreich?

Lösung

Dies ist ein häufig diskutiertes Thema.Der Nachteil beim Erstellen öffentlicher Felder in Objekten besteht darin, dass Sie keine Kontrolle über die darin festgelegten Werte haben.Bei Gruppenprojekten, bei denen viele Programmierer denselben Code verwenden, ist es wichtig, Nebenwirkungen zu vermeiden.Außerdem ist es manchmal besser, eine Kopie des Feldobjekts zurückzugeben oder es irgendwie umzuwandeln usw.Sie können solche Methoden in Ihren Tests nachahmen.Wenn Sie eine neue Klasse erstellen, werden möglicherweise nicht alle möglichen Aktionen angezeigt.Es ist wie eine defensive Programmierung – eines Tages könnten Getter und Setter hilfreich sein, und es kostet nicht viel, sie zu erstellen/zu verwenden.Deshalb sind sie manchmal nützlich.

In der Praxis verfügen die meisten Felder über einfache Getter und Setter.Eine mögliche Lösung würde so aussehen:

public property String foo;   
a->Foo = b->Foo;

Aktualisieren:Es ist höchst unwahrscheinlich, dass in Java 7 oder vielleicht jemals eine Eigenschaftsunterstützung hinzugefügt wird.Andere JVM-Sprachen wie Groovy, Scala usw. unterstützen diese Funktion jetzt.-Alex Miller

Andere Tipps

Es scheint, dass viele Java -Personen mit den Richtlinien der Sun Java -Codierung nicht vertraut sind, die besagen, dass es ziemlich angemessen ist, die öffentliche Instanzvariable zu verwenden, wenn die Klasse im Wesentlichen eine "Struktur" ist, wenn Java "Struktur" unterstützt (wenn es kein Verhalten gibt).

Die Leute neigen dazu, Getter und Setter der Java -Weg zu denken, als ob sie im Herzen von Java stehen.Das ist nicht so.Wenn Sie den Richtlinien für die Sun Java -Codierung in der Verwendung von öffentlichen Instanzvariablen in geeigneten Situationen folgen, schreiben Sie tatsächlich einen besseren Code, als es mit unnötigen Gettern und Settern zu überladen.

Java-Codekonventionen von 1999 und immer noch unverändert.

10.1 Bereitstellung des Zugriffs auf Instanz- und Klassenvariablen

Machen Sie keine Instanz- oder Klassenvariablen ohne guten Grund öffentlich.Instanzvariablen müssen häufig nicht explizit festgelegt oder abgerufen werden, was häufig als Nebeneffekt von Methodenaufrufen auftritt.

Ein Beispiel für geeignete öffentliche Instanzvariablen ist der Fall, dass die Klasse im Wesentlichen eine Datenstruktur ohne Verhalten ist. Mit anderen Worten: Wenn Sie eine Struktur anstelle einer Klasse verwendet hätten (sofern Java die Struktur unterstützt), wäre es angebracht, die Instanzvariablen der Klasse öffentlich zu machen.

http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-137265.html#177

http://en.wikipedia.org/wiki/Plain_old_data_structure

http://docs.oracle.com/javase/1.3/docs/guide/collections/designfaq.html#28

Benutzen Sie wirklich Ihren gesunden Menschenverstand.Wenn Sie so etwas haben wie:

public class ScreenCoord2D{
    public int x;
    public int y;
}

Dann macht es wenig Sinn, sie in Getter und Setter zu unterteilen.Auf andere Weise werden Sie eine X- und Y-Koordinate nie in ganzen Pixeln speichern.Getter und Setter werden Sie nur verlangsamen.

Andererseits mit:

public class BankAccount{
    public int balance;
}

Möglicherweise möchten Sie die Art und Weise, wie ein Kontostand berechnet wird, irgendwann in der Zukunft ändern.Hier sollten wirklich Getter und Setter verwendet werden.

Es ist immer besser zu wissen Warum Sie wenden bewährte Verfahren an, damit Sie wissen, wann es in Ordnung ist, die Regeln zu ändern.

Um Bedenken hinsichtlich der Veränderlichkeit auszuräumen, können Sie x und y als endgültig deklarieren.Zum Beispiel:

class Data {
  public final int x;
  public final int y;
  public Data( int x, int y){
    this.x = x;
    this.y = y;
  }
}

Beim Aufrufen von Code, der versucht, in diese Felder zu schreiben, wird bei der Kompilierung der Fehler „Feld x wird als endgültig deklariert“ angezeigt.nicht zuordenbar“.

Der Client-Code kann dann die von Ihnen in Ihrem Beitrag beschriebene „Kurzschrift“-Funktionalität haben

public class DataTest {
    public DataTest() {
        Data data1 = new Data(1, 5);
        Data data2 = new Data(2, 4);
        System.out.println(f(data1));
        System.out.println(f(data2));
    }

    public int f(Data d) {
        return (3 * d.x) / d.y;
    }

    public static void main(String[] args) {
        DataTest dataTest = new DataTest();
    }
}

Verwende nicht public Felder

Nicht verwenden public Felder, wenn Sie das interne Verhalten einer Klasse wirklich umschließen möchten.Nehmen java.io.BufferedReader Zum Beispiel.Es hat das folgende Feld:

private boolean skipLF = false; // If the next character is a line feed, skip it

skipLF wird in allen Lesemethoden gelesen und geschrieben.Was passiert, wenn eine externe Klasse, die in einem separaten Thread ausgeführt wird, den Status von böswillig ändert? skipLF mitten in einer Lektüre? BufferedReader wird auf jeden Fall durcheinander geraten.

Benutzen Sie es public Felder

Nimm das Point Klasse zum Beispiel:

class Point {
    private double x;
    private double y;

    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public double getX() {
        return this.x;
    }

    public double getY() {
        return this.y;
    }

    public void setX(double x) {
        this.x = x;
    }

    public void setY(double y) {
        this.y = y;
    }
}

Dies würde das Berechnen des Abstands zwischen zwei Punkten sehr mühsam machen.

Point a = new Point(5.0, 4.0);
Point b = new Point(4.0, 9.0);
double distance = Math.sqrt(Math.pow(b.getX() - a.getX(), 2) + Math.pow(b.getY() - a.getY(), 2));

Die Klasse verfügt über kein anderes Verhalten als einfache Getter und Setter.Es ist akzeptabel, öffentliche Felder zu verwenden, wenn die Klasse nur eine Datenstruktur darstellt und nicht über Folgendes verfügt: Und wird niemals Verhalten haben (Thin Getter und Setter). nicht hier betrachtetes Verhalten).Besser lässt es sich so schreiben:

class Point {
    public double x;
    public double y;

    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }
}

Point a = new Point(5.0, 4.0);
Point b = new Point(4.0, 9.0);
double distance = Math.sqrt(Math.pow(b.x - a.x, 2) + Math.pow(b.y - a.y, 2));

Sauber!

Aber erinnere dich:Ihre Klasse muss nicht nur verhaltensfrei sein, sie sollte es auch tun NEIN Grund, sich auch in Zukunft zu verhalten.


(Genau das ist es diese Antwort beschreibt.Zitieren „Codekonventionen für die Programmiersprache Java:10.Programmierpraktiken":

Ein Beispiel für geeignete öffentliche Instanzvariablen ist der Fall, dass die Klasse im Wesentlichen eine Datenstruktur ohne Verhalten ist.Mit anderen Worten, wenn Sie a verwendet hätten struct anstelle einer Klasse (sofern Java unterstützt wird). struct), dann ist es angebracht, die Instanzvariablen der Klasse öffentlich zu machen.

Die offizielle Dokumentation akzeptiert diese Praxis also auch.)


Auch wenn Sie besonders sicher sind, dass die oben genannten Mitglieder Point Die Klasse sollte unveränderlich sein, dann könnten Sie hinzufügen final Schlüsselwort, um es durchzusetzen:

public final double x;
public final double y;

Übrigens ist die Struktur, die Sie als Beispiel angeben, bereits in der Java-Basisklassenbibliothek vorhanden java.awt.Point.Es hat x und y als öffentliche Felder, Probieren Sie es selbst aus.

Wenn Sie wissen, was Sie tun, und andere in Ihrem Team davon wissen, ist es in Ordnung, öffentliche Felder zu haben.Aber Sie sollten sich nicht darauf verlassen, da sie Kopfschmerzen verursachen können, wie etwa bei Fehlern, die damit zusammenhängen, dass Entwickler Objekte verwenden, als wären sie Stack-zugewiesene Strukturen (Java-Objekte werden immer als Referenzen und nicht als Kopien an Methoden gesendet).

Re:aku, izb, John Topley...

Achten Sie auf Veränderlichkeitsprobleme ...

Es mag sinnvoll erscheinen, Getter/Setter wegzulassen.In einigen Fällen kann es tatsächlich in Ordnung sein.Das eigentliche Problem des hier gezeigten vorgeschlagenen Musters ist die Veränderlichkeit.

Das Problem besteht darin, dass Sie eine Objektreferenz übergeben, die nicht endgültige, öffentliche Felder enthält.Alles andere mit dieser Referenz ist frei, diese Felder zu ändern.Sie haben keine Kontrolle mehr über den Zustand dieses Objekts.(Überlegen Sie, was passieren würde, wenn Strings veränderbar wären.)

Schlimm wird es, wenn dieses Objekt ein wichtiger Teil des internen Zustands eines anderen Objekts ist. Sie haben gerade die interne Implementierung offengelegt.Um dies zu verhindern, muss stattdessen eine Kopie des Objekts zurückgegeben werden.Das funktioniert, kann jedoch zu einem massiven GC-Druck führen, da Tonnen von Einwegkopien erstellt werden.

Wenn Sie über öffentliche Felder verfügen, sollten Sie erwägen, die Klasse schreibgeschützt zu machen.Fügen Sie die Felder als Parameter zum Konstruktor hinzu und markieren Sie die Felder als endgültig.Stellen Sie andernfalls sicher, dass Sie den internen Status nicht offenlegen. Wenn Sie neue Instanzen für einen Rückgabewert erstellen müssen, stellen Sie sicher, dass dieser nicht übermäßig aufgerufen wird.

Sehen:"Effektives Java" von Joshua Bloch -- Punkt Nr. 13:Bevorzugen Sie Unveränderlichkeit.

PS:Denken Sie auch daran, dass alle JVMs heutzutage die getMethod nach Möglichkeit wegoptimieren, was zu nur einer einzigen Feldleseanweisung führt.

Ich habe dies in einigen Projekten versucht, basierend auf der Theorie, dass Getter und Setter den Code mit semantisch bedeutungslosem Mist vollstopfen und dass andere Sprachen scheinbar problemlos mit dem konventionsbasierten Verbergen von Daten oder der Aufteilung von Verantwortlichkeiten zurechtkommen (z. B.Python).

Wie bereits oben erwähnt, treten zwei Probleme auf, die nicht wirklich behoben werden können:

  • Nahezu jedes automatisierte Tool in der Java-Welt basiert auf der Getter/Setter-Konvention.Das Gleiche gilt, wie von anderen angemerkt, für JSP-Tags, Spring-Konfiguration, Eclipse-Tools usw.usw...Der Kampf gegen das, was Ihre Tools erwarten, ist ein Rezept für lange Google-Suchsitzungen auf der Suche nach dieser nicht standardmäßigen Art, Frühlingsbohnen zu initiieren.Die Mühe lohnt sich wirklich nicht.
  • Sobald Sie Ihre elegant codierte Anwendung mit Hunderten von öffentlichen Variablen haben, werden Sie wahrscheinlich mindestens eine Situation finden, in der diese nicht ausreichen – wenn Sie unbedingt Unveränderlichkeit benötigen oder ein Ereignis auslösen müssen, wenn die Variable gesetzt wird, oder wenn Sie sie auslösen möchten eine Ausnahme bei einer Variablenänderung, weil dadurch ein Objektstatus auf einen unangenehmen Wert gesetzt wird.Dann stehen Sie vor der wenig beneidenswerten Wahl, ob Sie Ihren Code überall dort, wo die Variable direkt referenziert wird, mit einer speziellen Methode vollstopfen oder ob Sie für drei der 1000 Variablen in Ihrer Anwendung eine spezielle Zugriffsform haben.

Und das im besten Fall, wenn man komplett in einem eigenständigen Privatprojekt arbeitet.Sobald Sie das Ganze in eine öffentlich zugängliche Bibliothek exportieren, werden diese Probleme noch größer.

Java ist sehr ausführlich, und das ist verlockend.Tu es nicht.

Wenn die Java-Methode die OO-Methode ist, dann verstößt die Erstellung einer Klasse mit öffentlichen Feldern gegen die Prinzipien zum Verbergen von Informationen, die besagen, dass ein Objekt seinen eigenen internen Status verwalten sollte.(Da ich Sie nicht nur mit Fachjargon beschimpfen möchte, besteht ein Vorteil des Verbergens von Informationen darin, dass die internen Abläufe einer Klasse hinter einer Schnittstelle verborgen sind. Nehmen wir an, Sie möchten den Mechanismus ändern, mit dem Ihre Strukturklasse eines ihrer Felder speichert. Sie müssen wahrscheinlich zurückgehen und alle Klassen ändern, die die Klasse verwenden ...)

Sie können auch nicht die Unterstützung für mit der JavaBean-Benennung kompatible Klassen nutzen, was schädlich wäre, wenn Sie sich beispielsweise dafür entscheiden, die Klasse in einer JavaServer-Seite zu verwenden, die mit Expression Language geschrieben wurde.

Der JavaWorld-Artikel Warum Getter- und Setter-Methoden böse sind Der Artikel könnte auch für Sie von Interesse sein, wenn Sie darüber nachdenken, wann Sie Zugriffs- und Mutatormethoden nicht implementieren sollten.

Wenn Sie eine kleine Lösung schreiben und die erforderliche Codemenge minimieren möchten, ist die Java-Methode möglicherweise nicht die richtige – ich schätze, es hängt immer von Ihnen und dem Problem ab, das Sie lösen möchten.

An dieser Art von Code ist nichts auszusetzen, vorausgesetzt, der Autor weiß Sie sind Strukturen (oder Datenshuttles) anstelle von Objekten.Viele Java-Entwickler können den Unterschied zwischen einem wohlgeformten Objekt (nicht nur einer Unterklasse von java.lang.Object, sondern einem WAHR Objekt in einer bestimmten Domäne) und eine Ananas.Ergo schreiben sie am Ende Strukturen, wenn sie Objekte benötigen und umgekehrt.

Das Problem bei der Verwendung des öffentlichen Feldzugriffs ist das gleiche wie bei der Verwendung einer neuen anstelle einer Factory-Methode: Wenn Sie Ihre Meinung später ändern, werden alle vorhandenen Aufrufer unterbrochen.Aus Sicht der API-Entwicklung ist es also normalerweise eine gute Idee, in den sauren Apfel zu beißen und Getter/Setter zu verwenden.

Ich gehe in die andere Richtung, wenn Sie den Zugriff auf die Klasse stark kontrollieren, beispielsweise in einer inneren statischen Klasse, die als interne Datenstruktur verwendet wird.In diesem Fall ist es möglicherweise viel klarer, den Feldzugriff zu verwenden.

Übrigens ist es nach Aussage von e-bartek meiner Meinung nach höchst unwahrscheinlich, dass die Unterstützung von Eigenschaften in Java 7 hinzugefügt wird.

Ich verwende dieses Muster häufig beim Erstellen privater innerer Klassen, um meinen Code zu vereinfachen, aber ich würde nicht empfehlen, solche Objekte in einer öffentlichen API verfügbar zu machen.Im Allgemeinen gilt: Je häufiger Sie Objekte in Ihrer öffentlichen API unveränderlich machen können, desto besser, und es ist nicht möglich, Ihr „strukturähnliches“ Objekt unveränderlich zu erstellen.

Abgesehen davon würde ich, selbst wenn ich dieses Objekt als private innere Klasse schreiben würde, immer noch einen Konstruktor bereitstellen, um den Code zum Initialisieren des Objekts zu vereinfachen.Drei Codezeilen zu haben, um ein brauchbares Objekt zu erhalten, ist einfach chaotisch.

Eine sehr, sehr alte Frage, aber lassen Sie mich noch einen kurzen Beitrag leisten.Mit Java 8 wurden Lambda-Ausdrücke und Methodenreferenzen eingeführt.Lambda-Ausdrücke können einfache Methodenverweise sein und keinen „wahren“ Körper deklarieren.Sie können ein Feld jedoch nicht in eine Methodenreferenz „umwandeln“.Daher

stream.mapToInt(SomeData1::x)

ist nicht legal, aber

stream.mapToInt(SomeData2::getX)

Ist.

Ich sehe keinen Schaden darin, wenn man weiß, dass es immer eine einfache Struktur sein wird und dass man ihr niemals Verhalten zuordnen möchte.

Dies ist eine Frage zum objektorientierten Design, nicht zur Sprache Java.Im Allgemeinen empfiehlt es sich, Datentypen innerhalb der Klasse auszublenden und nur die Methoden verfügbar zu machen, die Teil der Klassen-API sind.Wenn Sie interne Datentypen offenlegen, können Sie diese in Zukunft nie mehr ändern.Wenn Sie sie ausblenden, besteht Ihre einzige Verpflichtung gegenüber dem Benutzer in den Rückgabe- und Argumenttypen der Methode.

Hier erstelle ich ein Programm zur Eingabe von Namen und Alter von 5 verschiedenen Personen und führe eine Auswahlsortierung (nach Alter) durch.Ich habe eine Klasse verwendet, die als Struktur fungiert (wie die Programmiersprache C) und eine Hauptklasse, um den gesamten Vorgang auszuführen.Nachfolgend stelle ich den Code bereit ...

import java.io.*;

class NameList {
    String name;
    int age;
}

class StructNameAge {
    public static void main(String [] args) throws IOException {

        NameList nl[]=new NameList[5]; // Create new radix of the structure NameList into 'nl' object
        NameList temp=new NameList(); // Create a temporary object of the structure

        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));

        /* Enter data into each radix of 'nl' object */

        for(int i=0; i<5; i++) {
            nl[i]=new NameList(); // Assign the structure into each radix

            System.out.print("Name: ");
            nl[i].name=br.readLine();

            System.out.print("Age: ");
            nl[i].age=Integer.parseInt(br.readLine());

            System.out.println();
        }

        /* Perform the sort (Selection Sort Method) */

        for(int i=0; i<4; i++) {
            for(int j=i+1; j<5; j++) {
                if(nl[i].age>nl[j].age) {
                    temp=nl[i];
                    nl[i]=nl[j];
                    nl[j]=temp;
                }
            }
        }

        /* Print each radix stored in 'nl' object */

        for(int i=0; i<5; i++)
            System.out.println(nl[i].name+" ("+nl[i].age+")");
    }
}

Der obige Code ist fehlerfrei und getestet ...Kopieren Sie es einfach, fügen Sie es in Ihre IDE ein und ...Weißt du und was???:) :)

Sie können in Java eine einfache Klasse mit öffentlichen Feldern und ohne Methoden erstellen, aber es ist immer noch eine Klasse und wird syntaktisch und hinsichtlich der Speicherzuweisung immer noch wie eine Klasse behandelt.Es gibt keine Möglichkeit, Strukturen in Java wirklich zu reproduzieren.

Manchmal verwende ich eine solche Klasse, wenn ich mehrere Werte von einer Methode zurückgeben muss.Natürlich ist ein solches Objekt nur von kurzer Dauer und nur sehr eingeschränkt sichtbar, daher sollte es in Ordnung sein.

Wie bei den meisten Dingen gibt es die allgemeine Regel und dann gibt es spezifische Umstände.Wenn Sie eine geschlossene, erfasste Anwendung erstellen, damit Sie wissen, wie ein bestimmtes Objekt verwendet wird, können Sie mehr Freiheit nutzen, um Sichtbarkeit und/oder Effizienz zu fördern.Wenn Sie eine Klasse entwickeln, die von anderen Personen, die außerhalb Ihrer Kontrolle liegen, öffentlich verwendet werden soll, dann tendieren Sie zum Getter/Setter-Modell.Wie bei allen Dingen sollten Sie einfach Ihren gesunden Menschenverstand nutzen.Es ist oft in Ordnung, eine erste Runde mit Öffentlichkeiten zu machen und sie später in Getter/Setter umzuwandeln.

Mit der aspektorientierten Programmierung können Sie Zuweisungen oder Abrufe abfangen und ihnen eine Abfanglogik hinzufügen, was meiner Meinung nach der richtige Weg zur Lösung des Problems ist.(Die Frage, ob sie öffentlich oder geschützt oder paketgeschützt sein sollten, ist orthogonal.)

Somit beginnen Sie mit nicht abgefangenen Feldern mit dem richtigen Zugriffsqualifizierer.Wenn Ihre Programmanforderungen wachsen, fügen Sie Logik hinzu, um das zurückgegebene Objekt möglicherweise zu validieren, eine Kopie davon zu erstellen usw.

Die Getter/Setter-Philosophie verursacht in vielen einfachen Fällen Kosten, in denen sie nicht benötigt werden.

Ob der Aspektstil sauberer ist oder nicht, ist eher qualitativ.Ich würde es einfacher finden, nur die Variablen in einer Klasse zu sehen und die Logik separat anzuzeigen.Tatsächlich besteht die Daseinsberechtigung für die Apect-orientierte Programmierung darin, dass viele Anliegen bereichsübergreifend sind und ihre Unterteilung in den Klassenkörper selbst nicht ideal ist (die Protokollierung ist ein Beispiel – wenn Sie alles protokollieren möchten, erhält Java dies von Ihnen). Schreiben Sie eine ganze Reihe von Gettern und halten Sie sie synchron, aber AspectJ erlaubt Ihnen einen Einzeiler.

Das Thema IDE ist ein Ablenkungsmanöver.Es ist weniger das Tippen als vielmehr die Lese- und visuelle Verschmutzung, die durch Get/Sets entsteht.

Annotationen scheinen auf den ersten Blick der aspektorientierten Programmierung zu ähneln, erfordern jedoch eine ausführliche Aufzählung der Pointcuts durch Anhängen von Annotationen, im Gegensatz zu einer prägnanten Wildcard-ähnlichen Pointcut-Spezifikation in AspectJ.

Ich hoffe, dass die Bekanntheit von AspectJ Menschen davon abhält, sich vorzeitig für dynamische Sprachen zu entscheiden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top