Rechercher
 

Améliorations / Corrections

Vous avez des améliorations (ou des corrections) à proposer pour ce document : je vous remerçie par avance de m'en faire part, cela m'aide à améliorer le site.

Emplacement :

Description des améliorations :

Les classes enveloppantes (Wrapper classes)

Inférence de types sur les variables locales Synthèse sur les opérateurs Java



Accès rapide :
Méthodes de conversions pour vos types primitifs
Constantes et méthodes utilitaires
Auto-boxing et unboxing
Cas des collections génériques

Méthodes de conversions pour vos types primitifs

Java est un langage « totalement » objet : il en résulte que la notion de fonction n'existe pas en tant que telle. A la place, Java vous propose de coder des méthodes (statique ou non) : une méthode étant, en quelques sorte, une fonction, mais obligatoirement portée par une classe. Il n'existe donc pas, en Java, de fonctions travaillant sur les types primitifs, comme cela peut être le cas en C : à titre d'exemple le langage C fournit la fonction atoi (Ascii TO Integer) pour transformer une chaîne en un nombre entier. A la place il va nous falloir trouver des méthodes.

On le sait déjà, le package java.lang propose un certain nombre de classes : java.lang.String, java.lang.System, ... Mais nous l'avons aussi vu que dans ce même package se trouvent des classes associées aux types primitifs du langage Java : on parle de classes enveloppantes (ou de Wrapper Classes en anglais). Ce sont ces classes qui portent des méthodes permettant certaines conversions « primitif <=> chaînes de caractères ».

Le tableau suivant montre le lien entre chaque classe enveloppante et son type primitif associé ainsi que le nom des méthodes de conversion.

Classe enveloppante
Wrapper class
Type primitif associé Méthode de conversion
type primitif -> String
Méthode statique de conversion
String -> type primitif
java.lang.Byte byte String Byte.valueOf( value ).toString(); static byte parseByte( String str );
java.lang.Short short String Short.valueOf( value ).toString(); static short parseShort( String str );
java.lang.Integer int String Integer.valueOf( value ).toString(); static int parseInt( String str );
java.lang.Long long String Long.valueOf( value ).toString(); static long parseLong( String str );
java.lang.Float float String Float.valueOf( value ).toString(); static float parseFloat( String str );
java.lang.Double double String Double.valueOf( value ).toString(); static double parseDouble( String str );
java.lang.Boolean boolean String Boolean.valueOf( value ).toString(); static boolean parseBoolean( String str );
java.lang.Character char

Le programme suivant vous montre comment utiliser quelques-unes de ces méthodes. On cherche à y ajouter tous les entiers passés en paramètres du main (donc à partir de la ligne commande). Or, args est typé String [] : nos méthodes interviennent à ce niveau-là, car on cherche à transformer les chaînes en valeurs entières et à les ajouter entre elles. Au final on transforme le résultat entier en une chaîne de caractères (ce ne serait pas obligatoire, mais c'est pour l'exemple).

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
public class Adder {
            
    public static void main( String [] args ) {
        
        int accumulator = 0;
        
        for( String param : args ) {
            accumulator += Integer.parseInt( param );
        }
        
        String strAccumulator = Integer.valueOf( accumulator ).toString();
        System.out.println( strAccumulator );
        
    }
            
}
Exemple de conversions entre entiers et chaînes de caractères

Et voici les résultats produit par ce programme

$> java Demo 10 20 30
60
$> java Demo 9 8 7 6 5 4
39
$> 
en ligne 11, on transforme l'entier accumulator en une chaîne de caractère que l'on affiche en ligne 12. Néanmoins, on aurait aussi pu remplacer la ligne 11 par ce code.
 1 
String strAccumulator = "" + accumulator;
Solutions alternatives pour passer d'un entier à une chaîne de caractères

Constantes et méthodes utilitaires

Outre les méthodes relatives aux conversions de types, les classes enveloppantes proposent aussi tout un ensemble de constantes et de méthodes utilitaires pour manipuler les types associés. Nous en avons déjà vu un certain nombre dans les chapitres précédents.

Voici, pour rappel, quelques exemples de constantes que vous pouvez utiliser pour mieux connaître les bornes de vos types numériques entiers.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
public class Demo {

    public static void main( String [] args ) {

        System.out.println( Byte.MIN_VALUE );
        System.out.println( Byte.MAX_VALUE );

        System.out.println( Short.MIN_VALUE );
        System.out.println( Short.MAX_VALUE );

        System.out.println( Integer.MIN_VALUE );
        System.out.println( Integer.MAX_VALUE );

        System.out.println( Long.MIN_VALUE );
        System.out.println( Long.MAX_VALUE );

    }
    
}
Quelques constantes associées à vos types entiers

Ce qui affiche :

-128
127
-32768
32767
-2147483648
2147483647
-9223372036854775808
9223372036854775807

Voici maintenant quelques exemples de méthodes utilitaires : elles sont souvent statiques, donc portées par vos classes enveloppantes.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
public class Demo {

    public static void main( String [] args ) {

        char theChar1 = 'e';
        char theChar2 = '5';

        System.out.println( Character.isDigit( theChar1 ) );      // false
        System.out.println( Character.isDigit( theChar2 ) );      // true

        System.out.println( Character.isLetter( theChar1 ) );     // true
        System.out.println( Character.isLetter( theChar2 ) );     // false

        System.out.println( Character.isLowerCase( theChar1 ) );  // true
        System.out.println( Character.isLowerCase( theChar2 ) );  // false

        System.out.println( Character.isUpperCase( theChar1 ) );  // false
        System.out.println( Character.isUpperCase( theChar2 ) );  // false

        System.out.println( Character.isSpaceChar( theChar1 ) );  // false
        System.out.println( Character.isSpaceChar( theChar2 ) );  // false
        System.out.println( Character.isSpaceChar( ' ' ) );       // true

    }

}
Quelques méthodes statiques exposées par la classe java.lang.Character

Auto-boxing et unboxing

Bien que nous ayons déjà vu pas mal de choses sur ces classes enveloppantes, nous n'avons pas encore parlé de l'aspect le plus important. Effectivement, ces classes enveloppantes n'exposent pas que des méthodes statiques. Elles permettent aussi de créer des instances (des objets) qui vont pouvoir maintenir des valeurs. Par exemple, le code suivant permet de manipuler un entier, appelons le value et il vaudra 123 : mais regardez bien le type de la variable.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
public class Demo {

    public static void main( String [] args ) {
    
        // Integer value = new Integer( 123 );      // Possible, mais déprécié au profit de ...
        Integer value = Integer.valueOf( 123 );
        System.out.println( value );                // affiche 123
 
    }
    
}
Instanciation d'un objet de type Integer

Le code ci-dessus compile et s'exécute parfaitement depuis le JDK 1.0. Mais, avec le Java SE 5.0, les choses ont évoluées et la syntaxe s'en est trouvée largement simplifiée. Aujourd'hui, on peut écrire le programme ainsi.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
public class Demo {

    public static void main( String [] args ) {
    
        Integer value = 123;                // Auto-boxing
        System.out.println( value );        // Affiche 123
        
        // Il est même possible d'écrire
        value += 2;
        System.out.println( value );        // Affiche 125;
 
    }
    
}
Auto-boxing (depuis Java SE 5.0)

Regardez bien la ligne 5, en fait elle est strictement équivalente à la ligne 6 de l'avant dernier exemple de code. Le fait que l'appel à Integer.valueOf( 123 ) ne soit plus de votre responsabilité s'appelle « l'auto-boxing » : le passage de la données brute dans une instance de la classe Integer est fait automatiquement. Pour vous convaincre que l'appel est bien réalisé, voici le code désassemblé de la classe, via la commande javap -c.

$> javap -c Demo
Compiled from "Demo.java"
public class Demo {
  public Demo();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: bipush        123
       2: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
       5: astore_1
       6: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
       9: aload_1
      10: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      13: aload_1
      14: invokevirtual #5                  // Method java/lang/Integer.intValue:()I
      17: iconst_2
      18: iadd
      19: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      22: astore_1
      23: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      26: aload_1
      27: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      30: return
}
$> 

Du coup, la question que peut être vous vous posez est la suivante : peut-on indifféremment utiliser Integer value = 123; ou int value = 123; ? La réponse est NON ! Surtout pas. Pour des raisons que je vais passer pour le moment, disons simplement que l'utilisation des types primitifs sera un peu plus efficace en termes de temps d'exécution.

De plus, les données basées sur les classes enveloppantes sont des objets : ils sont donc gérés par références. Au contraire les types primitifs sont gérés par valeurs (on dit aussi par copies). Ce point à aussi des conséquences en termes de performances.

Cas des collections génériques

Comme je viens de le dire, il est préférable d'utiliser les types primitifs pour vos variables, plutôt que des classes enveloppantes et ce notamment pour des raisons de performances. Pour autant, il y a un cas ou vous n'aurez pas le choix : l'utilisation de classes génériques sur des types numériques (par exemple, l'utilisation des collections Java).

Effectivement, la généricité Java ne permet de travailler que sur des objets et l'utilisation de types primitifs n'est pas supportée par le compilateur. Ce programme ne compile pas.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
import java.util.ArrayList;
            
public class Demo {

    public static void main( String [] args ) {
    
        ArrayList<int> collection = new ArrayList<>();
        collection.add( 10 );
        collection.add( 20 );
        collection.add( 30 );
        for ( int value : collection ) {
            System.out.println( value );
        }
    
    }

}
Impossibilité d'appliquer une classe générique sur des types primitifs en Java

Et voici le message d'erreur produit par le compilateur.

$> javac Demo.java
Demo.java:7: error: unexpected type
        ArrayList<int> collection = new ArrayList<>();
                  ^
  required: reference
  found:    int
1 error
$> 

Par contre, ce programme compile parfaitement.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
import java.util.ArrayList;
            
public class Demo {

    public static void main( String [] args ) {
    
        ArrayList<Integer> collection = new ArrayList<>();
        collection.add( 10 );
        collection.add( 20 );
        collection.add( 30 );
        for ( int value : collection ) {
            System.out.println( value );
        }
    
    }

}
Utilisation conjointe d'une collection et d'une classe enveloppante

Les lignes 8,9 et 10 font appel à l'auto-boxing : les valeurs typées int sont automatiquement transformées en instances de la classe java.lang.Integer. La ligne 11 met en évidence le mécanisme inverse : l' « unboxing » qui permet l'extraction de la valeur primitive contenue dans une instance enveloppante. L'utilisation du type int devant la variable value est donc tout à fait légale.

l'utilisation de tableaux basés sur des types primitifs est, quand-à elle, tout à fait légale comme nous avons pu le vérifier dans un chapitre précédent relatif à l'utilisation de tableaux Java.


Inférence de types sur les variables locales Synthèse sur les opérateurs Java