Isso é realmente uma ampliação versus autoboxing?
-
08-06-2019 - |
Pergunta
Eu vi isso em uma resposta para outra pergunta, em referência às deficiências da especificação Java:
Existem mais deficiências e este é um tópico sutil.Verificar esse fora:
public class methodOverloading{ public static void hello(Integer x){ System.out.println("Integer"); } public static void hello(long x){ System.out.println("long"); } public static void main(String[] args){ int i = 5; hello(i); } }
Aqui, "longo" seria impresso (não verifiquei), porque o compilador escolhe a ampliação em vez do boxe automático.Tenha cuidado ao usar o boxe automático ou simplesmente não o use!
Temos certeza de que este é realmente um exemplo de ampliação em vez de autoboxing, ou é algo totalmente diferente?
Na minha varredura inicial, eu concordaria com a afirmação de que a saída seria "longa" com base em i
sendo declarado como um primitivo e não um objeto.No entanto, se você mudou
hello(long x)
para
hello(Long x)
a saída imprimiria "Inteiro"
O que realmente está acontecendo aqui?Não sei nada sobre compiladores/interpretadores de bytecode para java...
Solução
No primeiro caso, você tem uma conversão ampliada acontecendo.Isso pode ser visto ao executar o programa utilitário "javap" (incluído com o JDK), na classe compilada:
public static void main(java.lang.String[]);
Code:
0: iconst_ 5
1: istore_ 1
2: iload_ 1
3: i2l
4: invokestatic #6; //Method hello:(J)V
7: return
}
Claramente, você vê o I2L, que é o mnemônico para a instrução de alargamento do bytecode Integer-To-Long.Ver referência aqui.
E no outro caso, substituindo o "long x" pela assinatura do objeto "Long x", você terá este código no método principal:
public static void main(java.lang.String[]);
Code:
0: iconst_ 5
1: istore_ 1
2: iload_ 1
3: invokestatic #6; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
6: invokestatic #7; //Method hello:(Ljava/lang/Integer;)V
9: return
}
Então você vê que o compilador criou a instrução Integer.valueOf(int), para encaixar o primitivo dentro do wrapper.
Outras dicas
Sim, é, experimente em um teste.Você verá “longo” impresso.Ele está se ampliando porque o Java escolherá ampliar o int para um long antes de optar por autoboxá-lo para um número inteiro, portanto, o método hello(long) é escolhido para ser chamado.
Editar: a postagem original sendo referenciada.
Edição adicional:A razão pela qual a segunda opção imprimiria Inteiro é porque não há "alargamento" para um primitivo maior como opção, portanto DEVE encaixá-lo, portanto Inteiro é a única opção.Além disso, o java fará o autobox apenas para o tipo original, portanto, ocorreria um erro do compilador se você deixasse o hello (Long) e removesse o hello (Integer).
Outra coisa interessante neste exemplo é a sobrecarga do método.A combinação de ampliação de tipo e sobrecarga de método só funciona porque o compilador precisa tomar uma decisão sobre qual método escolher.Considere o seguinte exemplo:
public static void hello(Collection x){
System.out.println("Collection");
}
public static void hello(List x){
System.out.println("List");
}
public static void main(String[] args){
Collection col = new ArrayList();
hello(col);
}
Ele não usa o tipo de tempo de execução que é Lista, ele usa o tipo de tempo de compilação que é Coleção e, portanto, imprime "Coleção".
Eu encorajo você a ler Java eficaz, o que abriu meus olhos para alguns casos secundários do JLS.