91 votes

Pourquoi déclarer final une classe immuable en Java ?

J'ai lu que pour faire un classe immuable en Java, nous devons faire ce qui suit,

  1. Ne pas fournir de régleur
  2. Marquer tous les champs comme privés
  3. Rendre la classe finale

Pourquoi l'étape 3 est-elle nécessaire ? Pourquoi dois-je marquer la classe final ?

147voto

templatetypedef Points 129554

Si vous ne marquez pas la classe final il pourrait être possible pour moi de rendre soudainement votre classe apparemment immuable en fait mutable. Par exemple, considérez ce code :

public class Immutable {
     private final int value;

     public Immutable(int value) {
         this.value = value;
     }

     public int getValue() {
         return value;
     }
}

Maintenant, supposons que je fasse ce qui suit :

public class Mutable extends Immutable {
     private int realValue;

     public Mutable(int value) {
         super(value);

         realValue = value;
     }

     public int getValue() {
         return realValue;
     }
     public void setValue(int newValue) {
         realValue = newValue;
     }

    public static void main(String[] arg){
        Mutable obj = new Mutable(4);
        Immutable immObj = (Immutable)obj;              
        System.out.println(immObj.getValue());
        obj.setValue(8);
        System.out.println(immObj.getValue());
    }
}

Remarquez que dans mon Mutable j'ai surchargé le comportement de la sous-classe getValue pour lire un nouveau champ mutable déclaré dans ma sous-classe. En conséquence, votre classe, qui semble initialement immuable, ne l'est pas vraiment. Je peux passer cette Mutable partout où une Immutable est attendu, ce qui pourrait faire de très mauvaises choses au code en supposant que l'objet est vraiment immuable. Marquer la classe de base final empêche que cela se produise.

J'espère que cela vous aidera !

55voto

Laurence Gonsalves Points 50783

Contrairement à ce que beaucoup de gens pensent, rendre une classe immuable final es no nécessaire.

L'argument standard pour rendre les classes immuables final est que si vous ne le faites pas, les sous-classes peuvent ajouter la mutabilité, violant ainsi le contrat de la super-classe. Les clients de la classe supposeront l'immuabilité, mais seront surpris lorsque quelque chose mute sous leur nez.

Si on pousse cet argument à son extrême logique, alors todos les méthodes doivent être faites final En effet, dans le cas contraire, une sous-classe pourrait remplacer une méthode d'une manière qui ne serait pas conforme au contrat de sa super-classe. Il est intéressant de noter que la plupart des programmeurs Java considèrent cela comme ridicule, mais sont d'une certaine manière d'accord avec l'idée que les classes immuables doivent être final . Je soupçonne que cela a quelque chose à voir avec le fait que les programmeurs Java en général ne sont pas tout à fait à l'aise avec la notion d'immuabilité, et peut-être une sorte de pensée floue liée aux multiples significations de l'expression "immuable". final en Java.

Se conformer au contrat de votre super-classe n'est pas quelque chose qui peut ou doit toujours être appliqué par le compilateur. Le compilateur peut faire respecter certains aspects de votre contrat (par exemple, un ensemble minimal de méthodes et leurs signatures de type), mais de nombreuses parties des contrats typiques ne peuvent pas être appliquées par le compilateur.

L'immuabilité fait partie du contrat d'une classe. C'est un peu différent de certaines choses auxquelles les gens sont plus habitués, car il dit ce que la classe (et toutes les sous-classes) ne peut pas En effet, alors que je pense que la plupart des programmeurs Java (et généralement OOP) ont tendance à penser aux contrats comme étant liés à ce qu'une classe puede faire, pas ce qu'il ne peut pas faire.

L'immuabilité affecte également plus qu'une seule méthode - elle affecte l'instance entière - mais ce n'est pas vraiment très différent de la façon dont l equals y hashCode dans le travail de Java. Ces deux méthodes ont un contrat spécifique défini dans le document Object . Ce contrat énonce très soigneusement les choses que ces méthodes ne peut pas faire. Ce contrat est rendu plus spécifique dans les sous-classes. Il est très facile de surcharger equals o hashCode d'une manière qui viole le contrat. En fait, si vous surchargez une seule de ces deux méthodes sans l'autre, il y a de fortes chances que vous violiez le contrat. Il faut donc equals y hashCode ont été déclarés final en Object pour éviter cela ? Je pense que la plupart des gens diraient qu'ils ne devraient pas. De même, il n'est pas nécessaire de rendre les classes immuables final .

Cela dit, la plupart de vos classes, immuables ou non, sont probablement devrait être final . Voir Effective Java Deuxième édition Point 17 : "Concevoir et documenter pour l'héritage ou bien l'interdire".

Donc une version correcte de votre étape 3 serait : "Rendre la classe finale ou, lors de la conception pour le sous-classement, documenter clairement que toutes les sous-classes doivent continuer à être immuables."

23voto

Justin Ohms Points 1029

Ne notez pas l'examen final de toute la classe.

Il y a des raisons valables pour permettre à une classe immuable d'être étendue, comme indiqué dans certaines des autres réponses, donc marquer la classe comme finale n'est pas toujours une bonne idée.

Il est préférable de marquer vos propriétés privées et finales et si vous voulez protéger le "contrat", marquez vos getters comme finaux.

De cette façon, vous pouvez permettre à la classe d'être étendue (oui, peut-être même par une classe mutable), mais les aspects immuables de votre classe sont protégés. Les propriétés sont privées et ne peuvent pas être accédées, les récupérateurs de ces propriétés sont finaux et ne peuvent pas être surchargés.

Tout autre code qui utilise une instance de votre classe immuable pourra s'appuyer sur les aspects immuables de votre classe, même si la sous-classe qui lui est transmise est mutable sous d'autres aspects. Bien sûr, puisqu'il utilise une instance de votre classe, il ne sera même pas au courant de ces autres aspects.

5voto

digitaljoel Points 13557

S'il n'est pas final, n'importe qui peut étendre la classe et faire ce qu'il veut, comme fournir des setters, faire de l'ombre à vos variables privées et, en gros, la rendre mutable.

4voto

Nambari Points 42066

Cela contraint les autres classes à étendre votre classe.

La classe finale ne peut pas être étendue par d'autres classes.

Si une classe étend la classe que vous voulez rendre immuable, elle peut changer l'état de la classe en raison des principes d'héritage.

Précisez simplement "cela peut changer". Une sous-classe peut remplacer le comportement de la super-classe en utilisant une méthode de remplacement (comme templatetypedef/ réponse de Ted Hop).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X