Rode o programa abaixo e me expliquem por que é que 2 + 2 = 5.
import java.lang.reflect.*;
class DontMessWithImmutableClasses {
public static void main(String[] args)
throws IllegalAccessException, NoSuchFieldException {
// 2+2 = 5?
Field f = Integer.class.getDeclaredField ("value");
f.setAccessible (true);
Integer two = 2;
f.set (two, 3);
System.out.printf ("The value of two is now '%d' %n", two);
Integer x;
x = 2;
x = x + 2;
System.out.printf ("The value of x is now '%d' %n", x); // prints '5'
}
}
E o fonte compilado está correto… nem podemos culpar o compilador…
public static void main(String args[])
throws IllegalAccessException, NoSuchFieldException
{
Field f = java.lang.Integer.getDeclaredField("value");
f.setAccessible(true);
Integer two = Integer.valueOf(2);
f.set(two, Integer.valueOf(3));
System.out.printf("The value of two is now '%d' %n", new Object[] {
two
});
Integer x = Integer.valueOf(2);
x = Integer.valueOf(x.intValue() + 2);
System.out.printf("The value of x is now '%d' %n", new Object[] {
x
});
}
[quote=thingol]Rode o programa abaixo e me expliquem por que é que 2 + 2 = 5.
[code]
import java.lang.reflect.*;
class DontMessWithImmutableClasses {
public static void main(String[] args)
throws IllegalAccessException, NoSuchFieldException {
// 2+2 = 5?
Field f = Integer.class.getDeclaredField (“value”);
f.setAccessible (true);
Integer two = 2;
f.set (two, 3);
System.out.printf (“The value of two is now ‘%d’ %n”, two);
Integer x;
x = 2;
x = x + 2;
System.out.printf (“The value of x is now ‘%d’ %n”, x); // prints ‘5’
}
}
[/code][/quote]
Bem, deixa eu ver se entendi:
Integer two = 2;
Já que wrappers são imuatáveis, esse trecho deve criar uma “instância controlada” de Integer com o valor 2 de de referência de controle 2.
f.set (two, 3);
Esse trecho altera o valor para 3 da intância controlada de referência 2.
x = 2;
Recupera a instância de referência 2;
x = x + 2;
Cria uma nova instância de Integer. O valor vem do valor do objeto de referência 2 (com valor 3) mais 2;
Sendo assim:
Valor da referência 2, que é 3, mas 2 = 5.
Sim senhor.
Mas o curioso é ver que o resultado é 5 e não 6.
Como vocês viram, só descompilando é que dá para entender por quê.[/quote]
Só discordando do “só descompilando”.
Operações ariteméticas sempre são feitas com primitivos. Logo, o uso de primitivo em x + 2 é regra. Logo o valor tem que ser trasnformado de integer para int , somado a 2 e voltado a integer. é por isso que nunca se deve usar wrapper em calculos. Auto-(un)boxing não é a pedra filosofal
A outra coisa é saber que os wrapper primitivos fazem cache dos valores mais usado invocados via valueOf(). Ok, isso não é tão regra, mas isso vc descobre pelo primeiro pedaço do codigo.
O ponto é que não ha necessidade de descompilar para saber o resultado.
Wrappers costumam ser imutáveis, a menos que você force a natureza deles e use indevidamente reflection, como fiz acima.
O trecho acima mexe na instância do Integer que está no cache (não se esqueça - a JVM tem um cache de 256 Integers, que vão de -128 a +127), alterando diretamente o valor do campo “value”, que era 2 e passou a ser 3.
Na verdade, x = 2 é uma abreviação para x = Integer.valueOf (2), ou seja, ele pega no cache de 256 integers o Integer que seria correspondente ao valor 2. Como fizemos uma agressão ao meio ambiente, aham, como mudamos o valor do campo “value” para 3, então ele pegou um Integer cujo valor agora é 3. Que nojo!
A linha acima é uma abreviação para
x = Integer.valueOf (x.intValue() + 2). Como x.intValue() retorna 3 (devido à agressão ao meio ambiente), então 3 + 2 = 5, e então x recebe um Integer cujo value é 5.
Vejam como a explicação é complicada.
Moral da história: não abusem de reflection para tornar classes imutáveis em “mutáveis” na marra. Você pode acabar tendo problemas difíceis de entender.
esse cache na verdade é uma especie de mapa que possui um indice com o valor bruto primitivo e valorado a uma referencia wrapper correto?
e é ele que traz a performance para o autoboxing!
Field field = String.class.getDeclaredField("value");
field.setAccessible(true);
String s = "a";
field.set(s, "b".toCharArray());
String x = "a";
System.out.println(x + new String("a"));