Recherche…


Introduction

La compilation automatique est la conversion automatique effectuée par le compilateur Java entre les types primitifs et leurs classes d'encapsulation d'objet correspondantes. Exemple, conversion int -> Entier, double -> Double ... Si la conversion est inversée, cela s'appelle unboxing. En général, cela est utilisé dans les collections qui ne peuvent contenir que des objets, où les types primitifs de boxe sont nécessaires avant de les définir dans la collection.

Remarques

La programmation automatique peut avoir des problèmes de performance lorsqu'elle est fréquemment utilisée dans votre code.

Utilisation de int et Integer indifféremment

Lorsque vous utilisez des types génériques avec des classes d'utilitaires, vous pouvez souvent constater que les types de nombres ne sont pas très utiles lorsqu'ils sont spécifiés comme types d'objet, car ils ne sont pas égaux à leurs homologues primitifs.

List<Integer> ints = new ArrayList<Integer>();
Java SE 7
List<Integer> ints = new ArrayList<>();

Heureusement, les expressions évaluées par int peuvent être utilisées à la place d'un Integer lorsque cela est nécessaire.

for (int i = 0; i < 10; i++)
    ints.add(i);

Le ints.add(i); déclaration équivaut à:

ints.add(Integer.valueOf(i));

Et conserve les propriétés d' Integer#valueOf telles que la mise en cache des mêmes objets Integer par la JVM lorsqu'elle se trouve dans la plage de mise en cache des nombres.

Cela vaut également pour:

  • byte et Byte
  • short et Short
  • float et Float
  • double et Double
  • long et Long
  • char et Character
  • boolean et Boolean

Il faut toutefois être prudent dans les situations ambiguës. Considérez le code suivant:

List<Integer> ints = new ArrayList<Integer>();
ints.add(1);
ints.add(2);
ints.add(3);
ints.remove(1); // ints is now [1, 3]

L'interface java.util.List contient à la fois une méthode remove(int index) (méthode d'interface List ) et une remove(Object o) (méthode héritée de java.util.Collection ). Dans ce cas, aucune boxe n'a lieu et on remove(int index) .

Un autre exemple de comportement de code Java étrange causé par des entiers à base de boîtes de vitesses automatiques avec des valeurs comprises entre -128 et 127 :

Integer a = 127;
Integer b = 127;
Integer c = 128;
Integer d = 128;
System.out.println(a == b); // true
System.out.println(c <= d); // true
System.out.println(c >= d); // true
System.out.println(c == d); // false

Cela se produit car l'opérateur >= appelle implicitement intValue() qui renvoie int alors que == compare les références , pas les valeurs int .

Par défaut, Java met en cache les valeurs dans la plage [-128, 127] , de sorte que l'opérateur == fonctionne car les Integers dans cette plage font référence aux mêmes objets si leurs valeurs sont identiques. La valeur maximale de la plage pouvant être mise en cache peut être définie avec l'option -XX:AutoBoxCacheMax JVM. Donc, si vous exécutez le programme avec -XX:AutoBoxCacheMax=1000 , le code suivant s'imprimera true :

Integer a = 1000;
Integer b = 1000;
System.out.println(a == b); // true

Utilisation de booléen dans l'instruction if

En raison de la désactivation automatique, on peut utiliser un Boolean dans une instruction if :

Boolean a = Boolean.TRUE;
if (a) { // a gets converted to boolean
    System.out.println("It works!");
}

Cela fonctionne pour while , do while et l'état dans le for des déclarations aussi bien.

Notez que si le Boolean est null , une NullPointerException sera lancée dans la conversion.

Unboxing automatique peut conduire à NullPointerException

Ce code compile:

Integer arg = null;
int x = arg;

Mais il se bloquera à l'exécution avec une java.lang.NullPointerException sur la deuxième ligne.

Le problème est qu'un int primitif ne peut pas avoir de valeur null .

C'est un exemple minimaliste, mais dans la pratique, il se manifeste souvent sous des formes plus sophistiquées. Le NullPointerException n'est pas très intuitif et aide souvent peu à localiser de tels bogues.

En prenant soin de la mise en boîte automatique et de la désencapsulation automatique avec soin, assurez-vous que les valeurs non débrayées n'auront pas de valeurs null à l'exécution.

Mémoire et surcharge de calcul de l'autoboxing

L’autoboxing peut avoir une surcharge de mémoire importante. Par exemple:

Map<Integer, Integer> square = new HashMap<Integer, Integer>();
for(int i = 256; i < 1024; i++) {
    square.put(i, i * i); // Autoboxing of large integers
}

consommera généralement beaucoup de mémoire (environ 60 kb pour 6 k de données réelles).

De plus, les entiers encadrés nécessitent généralement des allers-retours supplémentaires dans la mémoire, ce qui rend les caches de processeur moins efficaces. Dans l'exemple ci-dessus, la mémoire accédée est répartie sur cinq emplacements différents qui peuvent se trouver dans des régions entièrement différentes de la mémoire: 1. l'objet HashMap , 2. l'objet Entry[] table , 3. l'objet Entry , 4. le entrys key object (boxing la clé primitive), 5. l'objet entry value (encadrant la value primitive).

class Example {
  int primitive; // Stored directly in the class `Example`
  Integer boxed; // Reference to another memory location
}

La lecture en boxed nécessite deux accès mémoire, n'accédant qu'à une primitive .

Lorsque vous obtenez des données de cette carte, le code apparemment innocent

int sumOfSquares = 0;
for(int i = 256; i < 1024; i++) {
    sumOfSquares += square.get(i);
}

est équivalent à:

int sumOfSquares = 0;
for(int i = 256; i < 1024; i++) {
    sumOfSquares += square.get(Integer.valueOf(i)).intValue();
}

En général, le code ci-dessus provoque la création et la récupération de Integer un objet Integer pour chaque opération Map#get(Integer) . (Voir la note ci-dessous pour plus de détails.)

Pour réduire ce surcoût, plusieurs bibliothèques offrent des collections optimisées pour les types primitifs ne nécessitant pas de boxe. En plus d'éviter la surcharge de boxe, ces collectes nécessiteront environ 4 fois moins de mémoire par entrée. Bien que Java Hotspot puisse optimiser l'autoboxing en travaillant avec des objets sur la pile au lieu du tas, il n'est pas possible d'optimiser la surcharge de mémoire et l'indirection de mémoire qui en résulte.

Les flux Java 8 ont également des interfaces optimisées pour les types de données primitifs, tels que IntStream qui ne nécessitent pas de boxe.

Remarque: un environnement d'exécution Java classique conserve un cache simple d' Integer et des autres objets wrapper primitifs utilisés par les méthodes factory valueOf et par la création automatique de boîtes aux lettres. Pour Integer , la plage par défaut de ce cache est comprise entre -128 et +127. Certaines machines virtuelles Java offrent une option de ligne de commande JVM pour modifier la taille / plage du cache.

Différents cas où Integer et int peuvent être utilisés indifféremment

Cas 1: En utilisant à la place des arguments de méthode.

Si une méthode nécessite un objet de classe wrapper en tant qu'argument.Alors de manière interchangeable, l'argument peut être passé à une variable du type primitif respectif et vice versa.

Exemple:

int i;
Integer j;
void ex_method(Integer i)//Is a valid statement
void ex_method1(int j)//Is a valid statement

Cas 2: Lors du passage des valeurs de retour:

Lorsqu'une méthode retourne une variable de type primitive, un objet de la classe wrapper correspondante peut être transmis comme valeur de retour de manière interchangeable et inversement.

Exemple:

int i;
Integer j;
int ex_method()
{...
return j;}//Is a valid statement
Integer ex_method1()
{...
return i;//Is a valid statement
}

Cas 3: lors des opérations.

Chaque fois que l'on effectue des opérations sur des nombres, la variable de type primitif et l'objet de la classe d'encapsulation respective peuvent être utilisés indifféremment.

int i=5;
Integer j=new Integer(7);
int k=i+j;//Is a valid statement
Integer m=i+j;//Is also a valid statement

Piège : N'oubliez pas d'initialiser ou d'attribuer une valeur à un objet de la classe wrapper.

Lorsque vous utilisez un objet de classe wrapper et une variable primitive de manière interchangeable, n'oubliez jamais ou omettez d'initialiser ou d'attribuer une valeur à l'objet de classe wrapper. Sinon, cela peut entraîner une exception de pointeur nul lors de l'exécution.

Exemple:

public class Test{
    Integer i;
    int j;
    public void met()
    {j=i;//Null pointer exception
    SOP(j);
    SOP(i);}   
    public static void main(String[] args)
    {Test t=new Test();
    t.go();//Null pointer exception
    }

Dans l'exemple ci-dessus, la valeur de l'objet est non affectée et non initialisée. Par conséquent, à l'exécution, le programme s'exécutera avec une exception de pointeur nul. Ainsi, la valeur de l'objet ne devrait jamais être non initialisée et non attribuée.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow