Tutorial Java SCJP – #15 Clase Wrapper pentru tipurile primitive

In Java exista 2 categorii importante de tipuri de date: referinte si primitive. De cele mai multe ori, valorile numerice, caracterele si valorile logice sunt utilizate ca primitive, deoarece sunt mai eficiente, ca viteza de procesare, sintaxa si efecte asupra memoriei. In ciuda acestui fapt, exista scenarii (cand folosesti Collections), când este necesar sa stochezi valori primitive in interiorul unor obiecte. Pentru aceasta nevoie, Java ofera un set de clase folosite la ambalarea (wrap) valorilor primitive intr-un obiect.

Acest topic face parte dintr-un tutorial Java 6 SCJP accesibil prin Tutorial Java 6 – Continut.

Fiecare tip primitiv de date are asociat in Java o clasa wrapper:

Tip primitiv

Clasa wrapper

booleanBoolean
charCharacter
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
Clasele wrapper Java pentru tipurile primitive

Pe lânga rolul principal (gestionarea valorilor primitive), clasele wrapper contin un set de metode utilitare utilizate pentru a converti valori la tipuri diferite sau de a transforma siruride caractere in valori numerice.

Ca o regula importanta, nu uitati ca aceste clase wrapper va permit sa definiti obiecte (gestionate prin referinte), care incapsuleaza valorile primitive.

 

Cum se definesc si cum se contruiesc obiecte wrapper

Obiectele wrapper sunt create folosind:

  • constructor clasei si operatorul new;
  • metoda statica a clasei wrapper: valueOf() .

Cu exceptia clasei Character, restul de clase wrapper ofera constructori care pot construi un obiect pe baza unei valoari numerice sau pe baza unui sir de caractere. De exemplu:

        
Integer intObject1 = new Integer(34);
Integer intObject2 = new Integer("35");

Boolean boolValue1 = new Boolean("true");
Boolean boolValue2 = new Boolean(true);

Character charObject = new Character('a');  //singura modalitate

Constructorul clasei Boolean ofera un constructor care accepta o valoare String care este interpretata ca fiind adevarata doar pentru valoarea case-insensitive true” (cum ar fi “True”, “TRUE”, “TrUe”, …). Orice alta valoare este convertita in false:

        
Boolean boolValue1 = new Boolean("true");	//true
Boolean boolValue2 = new Boolean(true);		//true
Boolean boolValue3 = new Boolean("True");	//true

Boolean boolValue4 = new Boolean("ZZZ");	//false

Metoda ValueOf() are 2 forme care primesc un String si un String cu o valoare radix reprezentând baza pentru valoarea numerica dorita:

        
Integer intObject3 = Integer.valueOf("36");
Integer intObject4 = Integer.valueOf("1001", 2);    //9 in baza 2

Metode utilitare pentru clasele wrapper

Fiind clase, acestea definesc, de asemenea, un set de metode utilitare ce pot fi folosite la prelucrarea valorilor numerice:

[byte|short|int|long|float|double]Value() – fiecare clasa wrapper are 6 metode de acest tip,  utilizate pentru a converti valoarea obiectului curent la orice tip numeric primitiv:

Float floatObject = new Float(520.3f);
byte byteValue = floatObject.byteValue();   	//este 8
short shortValue = floatObject.shortValue();    //este 520
  • parse[Byte|Short|Int|Long|Float|Double]() – fiecare clasa wrapper are o metoda statica (Integer are parseInt()) folosita pentru a converti un String intr-o valoare primitiva (de acelasi tip ca numele metodei);
  • ca si metoda valueOf(), aceste metode au 2 forme pentru ca accepta, de asemenea, baza numerica pentru conversie
  • daca sirul nu este valid, metoda arunca o exceptie de tipul NumberFormatException:
int intValue = Integer.parseInt("234");
long longValue = Integer.parseInt("ffff", 16);  //65535 in baza 16

int value = Integer.parseInt("12a");    //NumberFormatException

toString() – metoda mostenita de la clasa Object(ca orice alta clasa Java), care returneaza un String ce reprezinta valoarea numerica; este apelata in mod implicit; clasele wrapper numerice ofera de asemenea, o metoda statica toString(); Integer si Long ofera o a treia forma pentru toString(), care este statica si care permite conversia oricarei valori in baza 10 intr-o alta baza, indicata de valoarea radix:

Double doubleObject = new Double(123.4);
System.out.println("double value = " + doubleObject.toString());
System.out.println("boolean value " + Boolean.toString(true));
System.out.println("65000 in hex is "+Long.toString(65000, 16));

to[Binary|Octal|Hexadecimal]String() – metode statice definite numai in Long si Integer pentru conversia numerelor din baza 10 in baza 2, 8 sau 16:

String hexValue = Integer.toHexString(32);
String octValue = Integer.toOctalString(32);
String bynaryValue = Integer.toBinaryString(32);
System.out.println("32 este " + hexValue + " (16), " + 
                octValue + "(8) sau " + bynaryValue + "(2)");

Ce este autoboxing si unboxing

Incapsulare (boxing) este o caracteristica disponibila incepand cu Java 1.5, care permite programatorilor sa foloseasca clase wrapper intr-un mod mai convenabil. Fara aceasta facilitate, trebuia sa scrii o multime de cod pentru a face ceva simplu ca transferul de valori intre wrapper si primitive:

        Integer intObj1 = new Integer(23);
        int intPrimitive = intObj1.intValue();
        intPrimitive += 10;
        Integer intObj2 = new Integer(intPrimitive);

Cu autoboxing, acelasi lucru se face cu usurinta, deoarece compilatorul gestioneaza conversia pentru noi:

        Integer intObj1 = 23;		//autoboxing
        int intPrimitive = intObj1;	//unboxing
        intPrimitive++;
        Integer intObj2 = intPrimitive;	//autoboxing

Boxing-ul este procesul de incapsulare a unei valori primitive in interiorul unui obiect. Autoboxing-ul este procesul identic realizat fara apelul explicit al constructorilor. Unboxing-ul este procesul invers, care permite extragerea valorii fara un apel explicit la metodele [byte|short|int|long|float|double]Value().

Pentru ca, inainte de Java 1.5, nu exista autoboxing si unboxing, nu era posibil sa se (acum se poate) utilizeze un obiect Boolean intr-o expresie conditionala:

        Boolean boolValue = true;
        if(boolValue)   //unboxing
            System.out.println("Este adevarat !");

Topic SCJP – obiectele Wrapper sunt gestionate prin referinte

Datorita autoboxing-ului si unboxing-ului, se poate obtine o impresie falsa ca obiectele wrapper pot fi utilizate ca primitive. Dar, pentru examenul SCJP, nu uitati ca wrapper-ele sunt obiecte, ceea ce inseamna ca sunt gestionate prin referinte. Deci, amintiti-va ca:

  • = face shallow-copy; copiaza valori intre referinte si nu intre obiecte;
  • == compara valorile referintelor (adica adresele obiectelor);
  • equals() este folosit pentru a compara valorile obiectelor

Folosind regulile anterioare, exemplul urmator:

        Integer intObject1 = 2300;          //autoboxing
        Integer intObject2 = intObject1;    //shallow copy

        if(intObject1 == intObject2)
            System.out.println("Acelasi obiect !");
        else
            System.out.println("Obiecte diferite !");

        Integer intObject3 = 2300;          //autoboxing
        Integer intObject4 = 2300;          //autoboxing

        //comparare folosind ==
        if(intObject3 == intObject4)
            System.out.println("Acelasi obiect !");
        else
            System.out.println("Obiecte diferite !");
        
        //comparare folosind equals
        if(intObject3.equals(intObject4))
            System.out.println("Obiecte cu aceeasi valoare !");
        else
            System.out.println("Valori diferite !");

afiseaza:

Acelasi obiect !
Obiecte diferite !
Obiecte cu aceeasi valoare !

Topic SCJP – clasele wrapper sunt imuabile (immutable)

Pentru o descriere detaliata a conceptului de immutabil (imuabil) cititi Tutorial SCJP Java – #12 Immutable: String si Integer

Pornind de la definitia unei clase immutable, de fiecare data când modificati valoarea unui obiect wrapper este creat un obiect nou:

        Integer intObject1 = 2300;          //autoboxing
        Integer intObject2 = intObject1;    //shallow copy
        
	//incrementare a valorii si nu a referintei
        intObject1++;    
	//deoarece este immutable se obtine un nou obiect
                                    
        System.out.println(intObject1);

        if(intObject1 == intObject2)
            System.out.println("Acelasi obiect");
        else
            System.out.println("Obiecte diferite");

Exemplul anterior va afisa:

2301
Obiecte diferite

In spatele scenei, compilatorul despacheteaza obiectul intr-un primitiv, ii modifica valoarea si impacheteaza noua valoare intr-un obiect nou. Referinta va primi adresa obiectului nou creat.

Topic SCJP – clasele wrapper Boolean, Byte, Caracter, Shirt si Integer pot fi comparate cu == pentru valori mici

Din motive de eficienta, Java considera ca valorile intregi mici sunt folosite cu o frecventa mare si din acest motiv obiectele de tip Boolean, Byte, Caracter, Short si Integer pot fi comparate la nivel de valoare si cu operatorul == in loc de metoda equals().

Deci doua obiecte Boolean, Byte, Caracter, Short si Integer comparate cu == vor returna true daca valorile lor sunt egale. Aceasta regula este valabil si pentru:

  • orice obiect Boolean;
  • orice obiect Byte;
  • obiecte Character cu valori cuprinse intre \u0000 si \u007f  (\u007f este 127 in zecimal);
  • obiecte Short si Integer cu valori intre –128 si 127;

Urmatorul exemplu:

        Integer intObject1 = 10;    //autoboxing
        Integer intObject2 = 10;    //autoboxing
        
        if(intObject1 == intObject2)
            System.out.println("Valori egale");
        else
            System.out.println("Valori diferite");

        if(intObject1.equals(intObject2))
            System.out.println("Valori egale");
        else
            System.out.println("Valori diferite");

va afisa:

Valori egale
Valori egale

In exemplul anterior, daca folositi valori mai mari de 127, veti obtine mesaje diferite.

In practica evitati utilizarea operatorului == pentru a compara valori deoarece aceasta este o exceptie care poate genera probleme in alte situatii. De obicei, se recomanda utilizarea metodei equals().

Topicuri SCJP importante

  • toate clasele wrapper sunt imuabile (immutable);
  • referinta wrapper trebuie sa fie initializata inainte de a fi utilizata (regula valabila pentru orice referinta);
  • utilizarea operatorului == intre referinte wrapper este periculoasa (ATENTIE la valori mici deoarece se compara valori de obiecte si nu de referinte, cum este normal);
  • unboxing-ul si autoboxing-ul sunt implementate de catre compilator;

Acest topic face parte dintr-un tutorial Java 6 SCJP accesibil prin Tutorial Java 6 – Continut.