Struct comme des objets en Java


Est-ce complètement contre la façon Java de créer des objets de type struct?

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

Je peux voir une classe avec des accesseurs et des mutateurs qui ressemblent plus à Java.

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

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

    private int x;
    private int y;
}

La classe du premier exemple est notationnellement pratique.

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

Ce n'est pas aussi pratique.

// a function in a class
public int f(SomeData2 d) {
    return (3 * d.getX()) / d.getY();
}
Author: Cheekysoft, 2008-08-31

20 answers

C'est un sujet couramment discuté. L'inconvénient de la création de champs publics dans les objets est que vous n'avez aucun contrôle sur les valeurs qui y sont définies. Dans les projets de groupe où de nombreux programmeurs utilisent le même code, il est important d'éviter les effets secondaires. En outre, il est parfois préférable de renvoyer une copie de l'objet de field ou de le transformer d'une manière ou d'une autre, etc. Vous pouvez vous moquer de telles méthodes dans vos tests. Si vous créez une nouvelle classe, vous ne verrez peut-être pas toutes les actions possibles. C'est comme défensive programmation-un jour, les getters et les setters peuvent être utiles, et leur création/utilisation ne coûte pas cher. Ils sont donc parfois utiles.

En pratique, la plupart des champs ont des getters et des setters simples. Une solution possible ressemblerait à ceci:

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

Mise à jour: Il est très peu probable que le support de propriété soit ajouté dans Java 7 ou peut-être jamais. D'autres langages JVM comme Groovy, Scala, etc. prennent en charge cette fonctionnalité maintenant. - Alex Miller

 58
Author: Bartosz Bierkowski, 2016-07-10 16:58:39

Il semble que de nombreuses personnes Java ne soient pas familières avec les directives de codage Sun Java qui disent qu'il est tout à fait approprié d'utiliser une variable d'instance publique lorsque la classe est essentiellement une "Struct", si Java supportait" struct " (quand il n'y a pas de comportement).

Les gens ont tendance à penser que les getters et les setters sont la façon Java, comme s'ils étaient au cœur de Java. Ce n'est pas ainsi. Si vous suivez le Soleil Java Lignes directrices de codage, en utilisant des variables d'instance publiques dans des situations appropriées, vous êtes en fait, écrire un meilleur code que de l'encombrer avec des getters et des setters inutiles.

Conventions de code Java de 1999 et toujours inchangées.

10.1 Accès aux variables d'instance et de classe

Ne rendez aucune instance ou variable de classe publique sans raison valable. Souvent, les variables d'instance n'ont pas besoin d'être explicitement définies ou obtenues-cela se produit souvent comme un effet secondaire des appels de méthode.

Un exemple de variables d'instance publique appropriées c'est le cas où la classe est essentiellement une structure de données, sans comportement. En d'autres termes, si vous auriez utilisé une struct au lieu d'une classe (si Java supportait struct), alors il est approprié de créer les variables d'instance de la classe public.

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

 265
Author: developer.g, 2017-07-05 06:20:42

Utilisez vraiment le bon sens. Si vous avez quelque chose comme:

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

Ensuite, il est inutile de les envelopper dans des getters et des setters. Vous n'allez jamais stocker une coordonnée x, y en pixels entiers d'une autre manière. Les accesseurs et mutateurs ne vous ralentir.

D'autre part, avec:

public class BankAccount{
    public int balance;
}

Vous voudrez peut-être changer la façon dont un solde est calculé à un moment donné dans le futur. Cela devrait vraiment utiliser les accesseurs et mutateurs.

Il est toujours préférable de savoir pourquoi vous appliquez de bonnes pratiques, de sorte que vous sachiez quand il est acceptable de contourner les règles.

 214
Author: izb, 2015-08-21 08:03:42

Pour résoudre les problèmes de mutabilité, vous pouvez déclarer x et y comme définitifs. Par exemple:

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

Le code appelant qui tente d'écrire dans ces champs obtiendra une erreur de compilation de "le champ x est déclaré final; ne peut pas être attribué".

Le code client peut alors avoir la commodité "à main courte" que vous avez décrite dans votre message

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();
    }
}
 47
Author: Brian, 2008-10-15 03:58:41

Re: aku, izb, John Topley...

Attention aux problèmes de mutabilité...

Il peut sembler judicieux d'omettre les getters/setters. Il peut en fait être ok dans certains cas. Le vrai problème avec le modèle proposé ici est la mutabilité.

Le problème est une fois que vous passez une référence d'objet contenant des champs publics non finaux. Toute autre chose avec cette référence est libre de modifier ces champs. Vous n'avez plus aucun contrôle sur l'état de cet objet. (Pensez à ce qui arriverait si les chaînes étaient mutables.)

Cela devient mauvais lorsque cet objet est une partie importante de l'état interne d'un autre, vous venez d'exposer l'implémentation interne. Pour éviter cela, une copie de l'objet doit être renvoyée à la place. Cela fonctionne, mais peut provoquer une pression massive du GC à partir de tonnes de copies à usage unique créées.

Si vous avez des champs publics, envisagez de rendre la classe en lecture seule. Ajoutez les champs en tant que paramètres au constructeur et marquez les champs finaux. Sinon assurez vous que vous n'êtes pas exposer l'état interne, et si vous avez besoin de construire de nouvelles instances pour une valeur de retour, assurez-vous qu'elle ne sera pas appelée de manière excessive.

Voir: " Java efficace" par Joshua Bloch Item Article #13: Favoriser l'immutabilité.

PS: Gardez également à l'esprit que toutes les JVM de nos jours optimiseront la méthode getMethod si possible, ce qui entraînera une seule instruction de lecture de champ.

 9
Author: Mark Renouf, 2008-08-31 16:27:29

Ne pas utiliser public champs

N'utilisez pas de champs public lorsque vous voulez vraiment envelopper le comportement interne d'une classe. Prendre java.io.BufferedReader par exemple. Il a le champ suivant:

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

skipLF est lu et écrit dans toutes les méthodes de lecture. Et si une classe externe s'exécutant dans un thread séparé modifiait malicieusement l'état de skipLF au milieu d'une lecture? BufferedReader va certainement se détraquer.

Utilisez les champs public

Prenez cette classe Point pour exemple:

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

Cela rendrait le calcul de la distance entre deux points très douloureux à écrire.

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

La classe n'a aucun comportement autre que les getters et setters simples. Il est acceptable d'utiliser des champs publics lorsque la classe ne représente qu'une structure de données, et n'a pas, et n'aura jamais de comportement (les getters et setters minces sont pas considéré comme un comportement ici). Il peut être mieux écrit ceci chemin:

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

Propre!

Mais rappelez-vous: Non seulement votre classe doit être absente du comportement, mais elle devrait aussi avoir aucune raison d'avoir un comportement à l'avenir.

 8
Author: Krumia, 2017-06-08 06:21:49

En passant, la structure que vous donnez comme exemple existe déjà dans la bibliothèque de classes de base Java en tant que java.awt.Point. Il a x et y comme champs publics, vérifiez-le par vous-même .

Si vous savez ce que vous faites et que d'autres membres de votre équipe le savent, alors il est normal d'avoir des champs publics. Mais vous ne devriez pas vous y fier car ils peuvent causer des maux de tête comme dans les bogues liés aux développeurs utilisant des objets comme s'ils étaient des structures allouées à la pile (les objets java sont toujours envoyés aux méthodes comme références et non en tant que copies).

 7
Author: Spoike, 2008-08-31 14:24:06

J'ai essayé cela dans quelques projets, sur la théorie selon laquelle les getters et les setters encombrent le code avec un cruft sémantiquement vide de sens, et que d'autres langages semblent faire très bien avec la dissimulation de données basée sur des conventions ou le partitionnement des responsabilités (par exemple python).

Comme d'autres l'ont noté ci-dessus, il y a 2 problèmes que vous rencontrez, et ils ne sont pas vraiment réparables:

  • À peu près n'importe quel outil automatisé dans le monde java repose sur la convention getter/setter. Idem pour, comme indiqué par d'autres, les balises jsp, la configuration spring, les outils eclipse, etc. etc... Lutter contre ce que vos outils attendent de voir est une recette pour de longues sessions à la traîne via Google en essayant de trouver cette façon non standard d'initier les haricots de printemps. Vraiment pas la peine.
  • Une fois que vous avez votre application élégamment codée avec des centaines de variables publiques, vous trouverez probablement au moins une situation où elles sont insuffisantes - où vous avez absolument besoin d'immuabilité, ou vous devez déclenchez un événement lorsque la variable est définie, ou vous souhaitez lancer une exception sur un changement de variable car il définit un état d'objet sur quelque chose de désagréable. Vous êtes alors coincé avec les choix peu enviables entre encombrer votre code avec une méthode spéciale partout où la variable est directement référencée, ayant un formulaire d'accès spécial pour 3 des 1000 variables de votre application.

Et c'est dans le meilleur des cas de travailler entièrement dans un privé autonome projet. Une fois que vous exportez le tout vers une bibliothèque accessible au public, ces problèmes deviendront encore plus importants.

Java est très verbeux, et c'est une chose tentante à faire. Ne pas le faire.

 7
Author: Steve B., 2008-12-01 20:50:48

Si la méthode Java est la méthode OO, alors oui, la création d'une classe avec des champs publics brise les principes autour de la dissimulation d'informations qui disent qu'un objet doit gérer son propre état interne. (Donc, comme je ne me contente pas de vous lancer du jargon, un avantage de la dissimulation d'informations est que le fonctionnement interne d'une classe est caché derrière une interface-disons que vous vouliez changer le mécanisme par lequel votre classe struct a enregistré l'un de ses champs, vous devrez probablement revenir en arrière et changer toutes les classes utilisez la classe...)

Vous ne pouvez pas non plus profiter de la prise en charge des classes compatibles JavaBean naming, ce qui fera mal si vous décidez, par exemple, d'utiliser la classe dans une page JavaServer écrite en utilisant le langage Expression.

L'article JavaWorld Pourquoi les méthodes Getter et Setter sont mauvaises L'article pourrait également vous intéresser en pensant à quand ne pas implémenter les méthodes accessor et mutator.

Si vous écrivez une petite solution et que vous souhaitez minimiser le quantité de code impliquée, la manière Java peut ne pas être la bonne - je suppose que cela dépend toujours de vous et du problème que vous essayez de résoudre.

 4
Author: brabster, 2008-08-31 09:39:58

Il n'y a rien de mal à ce type de code, à condition que l'auteur sait ce sont des structures (ou des navettes de données) au lieu d'objets. Beaucoup de développeurs Java ne peuvent pas faire la différence entre un objet bien formé (pas seulement une sous-classe de java.lang.Mais un objet true dans un domaine spécifique) et un ananas. Ergo, ils finissent par écrire des structures quand ils ont besoin d'objets et vice-versa.

 3
Author: luis.espinal, 2010-04-15 02:59:08

Le problème avec l'utilisation de public field access est le même que l'utilisation de new au lieu d'une méthode factory - si vous changez d'avis plus tard, tous les appelants existants sont cassés. Donc, d'un point de vue de l'évolution de l'API, c'est généralement une bonne idée de mordre la balle et d'utiliser des getters/setters.

Un endroit où je vais dans l'autre sens est lorsque vous contrôlez fortement l'accès à la classe, par exemple dans une classe statique interne utilisée comme structure de données interne. Dans ce cas, cela pourrait être beaucoup plus clair pour utiliser le champ de l'accès.

En passant, sur l'affirmation d'e-bartek, il est très peu probable que l'OMI ajoute le support de propriété dans Java 7.

 2
Author: Alex Miller, 2008-09-15 16:21:20

J'utilise fréquemment ce modèle lors de la construction de classes internes privées pour simplifier mon code, mais je ne recommanderais pas d'exposer de tels objets dans une API publique. En général, plus vous pouvez rendre les objets de votre API publique immuables, mieux c'est, et il n'est pas possible de construire votre objet 'struct-like' de manière immuable.

En aparté, même si j'écrivais cet objet en tant que classe interne privée, je fournirais toujours un constructeur pour simplifier le code pour initialiser le objet. Avoir 3 lignes de code pour obtenir un objet utilisable quand on le fera est juste en désordre.

 2
Author: Kris Nuttycombe, 2008-10-15 04:28:04

Une très-très vieille question, mais permettez-moi d'apporter une autre courte contribution. Java 8 a introduit des expressions lambda et des références de méthodes. Les expressions Lambda peuvent être des références de méthode simples et ne pas déclarer un corps "vrai". Mais vous ne pouvez pas "convertir" un champ en référence de méthode. Ainsi

stream.mapToInt(SomeData1::x)

N'est pas légal, mais

stream.mapToInt(SomeData2::getX)

Est.

 2
Author: Lyubomyr Shaydariv, 2014-07-10 15:03:55

Je ne vois pas le mal si vous savez que ce sera toujours une structure simple et que vous ne voudrez jamais y attacher un comportement.

 1
Author: John Topley, 2008-08-31 09:20:18

Ceci est une question sur la conception orientée objet, pas Java le langage. Il est généralement recommandé de masquer les types de données dans la classe et d'exposer uniquement les méthodes qui font partie de l'API de classe. Si vous exposez des types de données internes, vous ne pourrez jamais les modifier à l'avenir. Si vous les masquez, votre seule obligation envers l'utilisateur est les types de retour et d'argument de la méthode.

 1
Author: Jonathan, 2008-08-31 10:13:01

Ici, je crée un programme pour entrer le nom et l'âge de 5 personnes différentes et effectuer un tri de sélection (âge sage). J'ai utilisé une classe qui agissent comme une structure (comme le langage de programmation C) et une classe principale pour effectuer l'opération complète. En vertu des présentes je me suis remise avec le code...

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+")");
    }
}

Le code ci-dessus est sans erreur et testé... Il suffit de copier et coller dans votreE et ... Vous le savez, et quoi??? :)

 1
Author: Avik Kumar Goswami, 2013-02-19 09:36:07

Vous pouvez créer une classe simple avec des champs publics et aucune méthode en Java, mais c'est toujours une classe et est toujours gérée syntaxiquement et en termes d'allocation de mémoire comme une classe. Il n'y a aucun moyen de reproduire véritablement les structures en Java.

 0
Author: cblades, 2008-10-15 04:35:09

Parfois, j'utilise une telle classe, quand j'ai besoin de renvoyer plusieurs valeurs d'une méthode. Bien sûr, un tel objet est de courte durée et avec une visibilité très limitée, il devrait donc être OK.

 0
Author: PhiLho, 2008-11-23 18:43:14

Comme pour la plupart des choses, il y a la règle générale et ensuite il y a des circonstances spécifiques. Si vous faites une application fermée et capturée afin de savoir comment un objet donné va être utilisé, vous pouvez exercer plus de liberté pour favoriser la visibilité et/ou l'efficacité. Si vous développez une classe qui va être utilisée publiquement par d'autres personnes hors de votre contrôle, penchez-vous vers le modèle getter/setter. Comme pour toutes choses, il suffit d'utiliser le bon sens. Il est souvent correct de faire un tour initial avec publics, puis changez-les en getter/setters plus tard.

 0
Author: Evvo, 2010-04-25 04:13:52

La programmation orientée aspect vous permet de piéger des affectations ou des récupérations et d'y attacher une logique d'interception, ce que je propose est la bonne façon de résoudre le problème. (La question de savoir s'ils doivent être publics, protégés ou protégés par des paquets est orthogonale.)

Vous commencez donc avec des champs non perceptés avec le qualificateur d'accès droit. À mesure que les exigences de votre programme augmentent, vous attachez une logique pour peut-être valider, faire une copie de l'objet renvoyé, etc.

Le getter / setter la philosophie impose des coûts à un grand nombre de cas simples où ils ne sont pas nécessaires.

Que le style aspect soit plus propre ou non est quelque peu qualitatif. Je trouverais facile de voir uniquement les variables dans une classe et de voir la logique séparément. En fait, la raison d'être de la programmation orientée Apect est que de nombreuses préoccupations sont transversales et que les compartimenter dans le corps de la classe lui-même n'est pas idéal ( la journalisation en étant un exemple if si vous voulez enregistrer tous les gets Java veut que vous écriviez un tout un tas de getters et de les garder en synchronisation, mais AspectJ vous permet un one-liner).

Le problème de l'E est un hareng rouge. Ce n'est pas tant la frappe que la lecture et la pollution visuelle qui découlent de get/sets.

Les annotations semblent similaires à la programmation orientée aspect à première vue, mais elles vous obligent à énumérer de manière exhaustive les pointcuts en attachant des annotations, par opposition à une spécification pointcut concise de type wild-card dans AspectJ.

J'espère la prise de conscience d'AspectJ empêche les gens de s'installer prématurément sur les langues dynamiques.

 0
Author: necromancer, 2011-12-19 19:02:53