web-dev-qa-db-fra.com

Pourquoi les classes de wrapper Java sont-elles immuables?

Je connais les raisons habituelles qui s’appliquent aux classes générales immuables, à savoir

  1. ne peut pas changer comme un effet secondaire
  2. facile de raisonner sur leur état
  3. intrinsèquement thread-safe
  4. pas besoin de fournir la méthode de copie par constructeur/copie en usine
  5. mise en cache d'instance
  6. pas besoin de copies défensives.

Cependant, les classes wrapper représentent des types primitifs et les types primitifs sont mutables. Alors pourquoi les classes wrapper ne sont-elles pas mutables?

20
shrini1000

Cependant, les classes wrapper représentent des types primitifs, et les types primitifs (à l'exception de String) peuvent être modifiés.

Tout d'abord, String n'est pas un type primitif.

Deuxièmement, il est insensé de parler de mutabilité des types primitifs. Si vous modifiez la valeur d'un variable comme ceci:

int x = 5;
x = 6;

Cela ne change pas le nombre 5 - cela change la valeur de x.

Bien que les types de wrapper aient pu être rendus mutables, cela aurait été agaçant de le faire, à mon avis. J'utilise fréquemment des collections en lecture seule de ces types et je ne souhaite pas qu'elles soient modifiables. Très occasionnellement, je veux un équivalent mutable, mais dans ce cas, il est assez facile d'en trouver un, ou d'utiliser les classes Atomic*.

Je souhaite que Date et Calendar soient immuables beaucoup plus souvent que je ne veux que Integer soit mutable ... (Bien sûr, je cherche plutôt Joda Time à la place, mais l'un des avantages de Joda Time est immuabilité.)

29
Jon Skeet

Il existe également des wrappers thread-safe mutables pour certains types.

AtomicBoolean
AtomicInteger
AtomicIntegerArray
AtomicLong
AtomicLongArray
AtomicReference - can wrap a String.
AtomicReferenceArray

Plus quelques emballages exotiques

AtomicMarkableReference - A reference and boolean
AtomicStampedReference - A reference and int
9
Peter Lawrey

Pour votre information: si vous voulez des classes de détenteurs mutables, vous pouvez utiliser les classes Atomic * du paquet Java.util.concurrent, par exemple. AtomicInteger, AtomicLong

6
Sean Patrick Floyd

Voici un exemple où il serait très mauvais quand Integer serait mutable

class Foo{
    private Integer value;
    public set(Integer value) { this.value = value; }
}

/* ... */

Foo foo1 = new Foo();
Foo foo2 = new Foo();
Foo foo3 = new Foo();
Integer i = new Integer(1);
foo1.set(i);
++i;
foo2.set(i);
++i;
foo3.set(i);

Quelles sont les valeurs de foo1, foo2 et foo3 maintenant? Vous vous attendriez à ce qu'ils soient 1, 2 et 3. Mais quand Integer serait mutable, ils seraient maintenant tous 3, car Foo.value pointerait tous vers le même objet Integer.

4
Philipp

Cependant, les classes wrapper représentent des types primitifs, et les types primitifs (à l'exception de String) peuvent être modifiés.

Non, ils ne le sont pas (et String n'est pas une primitive). Mais comme les types primitifs ne sont de toute façon pas des objets, on ne peut pas vraiment les appeler mutables/immuables.

Quoi qu’il en soit, le fait que les classes de wrapper soient immuables est une décision de conception (une bonne OMI.) Thye aurait simplement pu être rendu mutable, ou des alternatives mutables fournies aussi (en effet, plusieurs bibliothèques le proposent, et d’autres langages le font par défaut.)

4
Michael Berry

Toute instance d'objet qui a tout aspects mutables doivent avoir un unique identité; sinon, d'autres instances d'objets qui, à un moment donné, étaient identiques à tous égards, à l'exception de leur identité, pourraient, à un autre moment, différer sous leur aspect mutable. Dans de nombreux cas, cependant, il est utile que les types n’aient pas d’identité - pour pouvoir passer un "4" sans avoir à se soucier de which "4" on passe. Bien qu’il soit parfois utile d’avoir un wrapper modifiable d’un type primitif ou immuable, il existe de nombreux autres cas où il est utile d’avoir un type où toutes les instances qui contiennent les mêmes données à un moment donné peuvent être considérées comme telles. interchangeable.

3
supercat

Les classes wrapper sont immuables car il n’a aucun sens d’être mutables.

Considérons le code suivant:

int n = 5;
n = 6;
Integer N = new Integer(n);

Au début, cela semble simple si vous pouvez changer la valeur de N, tout comme vous pouvez changer la valeur de n.

Mais en réalité, N n’est pas une enveloppe pour n, mais une enveloppe pour 6! Regardez à nouveau la ligne suivante:

Integer N = new Integer(n);

Vous transmettez en réalité la valeur de n, qui est 6, à N. Et, puisque Java est une valeur donnée, vous ne pouvez pas transmettre n en N, pour que N soit un wrapper à n.

Donc, si nous avons ajouté une méthode set au wrapper:

Integer N = new Integer(n);
N.setValue(7);
print(N); // ok, now it is 7
print(n); // oops, still 6!

La valeur de n ne sera pas modifiée et ce sera déroutant!

Conclusion:

  1. les classes wrapper sont des wrappers de valeurs, pas des wrappers de variables.

  2. ce sera déroutant si vous avez ajouté une méthode set.

  3. si vous savez qu'il s'agit d'un wrapper d'une valeur, vous ne demanderez plus de méthode set. Par exemple, vous ne ferez pas "6.setValue (7)".

  4. il est impossible de transformer un wrapper en une variable en Java.

1
Henry

Par exemple, considérons le programme Java suivant:

class WhyMutable 
{
    public static void main(String[] args) 
    {
        String name = "Vipin";
        Double sal = 60000.00;
        displayTax(name, sal);
    }

    static void displayTax(String name, Double num) {
        name = "Hello " + name.concat("!");
        num = num * 30 / 100;
        System.out.println(name + " You have to pay tax $" + num);
    }
}

Result: Hello Vipin! You have to pay tax $18000.0

C’est le cas également des paramètres de classe encapsuleur passe par référence. De plus, si les chaînes et les classes d'emballage ne sont pas finales, n'importe qui peut les étendre et écrire leur propre code pour modifier les données primitives enveloppées. Donc, pour maintenir l’intégrité des données, les variables que nous utilisons pour le stockage des données doivent être en lecture seule,

c'est-à-dire que les classes Strings et Wrapper doivent être définitives et immuables et que la fonctionnalité «passage par référence» ne doit pas être fournie.

0
Vipin Jain

Les types primitifs sont modifiables, mais ils ne peuvent pas être partagés. Autrement dit, deux sections de code ne feront jamais référence à la même variable int (elles sont toujours passées par valeur). Donc, vous pouvez changer votre copie et personne d'autre ne voit le changement, et vice versa. Comme Phillip le montre dans sa réponse, ce ne serait pas le cas avec les classes de wrapper mutables. Donc, je suppose qu'ils avaient le choix lorsque les types de données primitifs encapsulés étaient entre:

correspondant au fait que vous pouvez changer la valeur d'un type primitif,

versus

cela correspond au fait que les types primitifs peuvent être transmis et qu'aucun utilisateur ne verra les modifications apportées par aucun autre utilisateur des données.

Et ils ont choisi le dernier, ce qui nécessitait l'immuabilité.

0
Scooter