Rode o programa abaixo e me expliquem por que é que 2 + 2 = 5.
importjava.lang.reflect.*;classDontMessWithImmutableClasses{publicstaticvoidmain(String[]args)throwsIllegalAccessException,NoSuchFieldException{// 2+2 = 5?Fieldf=Integer.class.getDeclaredField("value");f.setAccessible(true);Integertwo=2;f.set(two,3);System.out.printf("The value of two is now '%d' %n",two);Integerx;x=2;x=x+2;System.out.printf("The value of x is now '%d' %n",x);// prints '5'}}
Atenção, pessoal mais desesperado: isso não cai na certificação. É só uma curiosidade, e só deve funcionar a partir do Java 5.0.
Mas escrevi isso só para desestimulá-los a tentar usar wrappers como classes mutáveis; isso pode dar confusão, como a que vocês viram acima.
tnaires
Foi por causa dessa atribuição aqui?
f.set(two,3);
A linha está mudando o valor para 3.
B
bKn
Você muda o valor de 2 no cache, é isso?
C
clone_zealot
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
});
}
A
andreban
AahHAahAHa
Mto boa!!
T
thingol
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ê.
Mero_Aprendiz
thingol:
Rode o programa abaixo e me expliquem por que é que 2 + 2 = 5.
importjava.lang.reflect.*;classDontMessWithImmutableClasses{publicstaticvoidmain(String[]args)throwsIllegalAccessException,NoSuchFieldException{// 2+2 = 5?Fieldf=Integer.class.getDeclaredField("value");f.setAccessible(true);Integertwo=2;f.set(two,3);System.out.printf("The value of two is now '%d' %n",two);Integerx;x=2;x=x+2;System.out.printf("The value of x is now '%d' %n",x);// prints '5'}}
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.
Estou certo?
[]'s
JL
B
bKn
Que massa, 128 pra cima não funciona… rs
Reflection é traiçoeiro.
sergiotaborda
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ê.
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.
T
thingol
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.
benflodin
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!
T
thingol
Basta olhar o fonte de java.lang.Integer, que está no arquivo /java/lang/Integer.java dentro de src.zip que está no diretório do JDK.
Lá você pode ver que existe um array estático de Integer, com 256 posições, contendo valores pré-alocados de Integer que vão de -128 a +127.
O método valueOf (que é chamado pelo autoboxing) faz mais ou menos isto aqui: