Eu não entendo como a alça compilador é o seguinte código como ele gera Teste enquanto eu estava esperando um erro.
List<Integer> b = new ArrayList<Integer>();
List a = b;
a.add("test");
System.out.println(b.get(0));
Eu estava esperando que alguém poderia me dizer os exatos passos que o compilador faz quando executar o código para que eu possa entender a saída. O meu entendimento atual é que:
- O compilador verifica durante o tempo de compilação se um método add que suporta o tipo de argumento existe na lista de classe que é add (Object e) como sua matéria-digitado.
- No entanto, durante a execução que tenta chamar add (Object e) do objeto real List <Integer> que não detém este método como o objeto real não é matéria-digitado e, em vez detém o método add (Integer e) .
Se não houver um add (Object e) método no objeto real List <Integer> como se ainda de alguma forma adicionar uma String para a lista de inteiros?
Você está muito perto. As verificações de tempo de compilação todos pan out:
a
é do tipo List
que a chamada
a.add("test");
garimpa para fora. b
é de (tempo de compilação) tipo ArrayList<Integer>
assim
b.get(0)
cheques para fora também. Note-se que as verificações são feitas apenas contra os tipos em tempo de compilação das variáveis . Quando o compilador vê a.add("test")
ele não sabe o valor tempo de execução do objeto referenciado pela variável a
. Em geral, ele realmente não pode (não há um resultado em ciência da computação teórica sobre este), embora a análise tipo de controle de fluxo pode pegar muitas dessas coisas. Linguagens como typescript pode fazer coisas incríveis em tempo de compilação.
Agora você pode assumir que em tempo de execução tais coisas poderiam ser verificado. Infelizmente, em Java que não pode. Java apaga tipos genéricos. Encontre um artigo sobre Java tipo de apagamento para os detalhes. O TL; DR é que uma List<Integer>
em tempo de compilação se torna uma matéria List
em tempo de execução. A JVM não tem uma maneira de "reificam" genéricos (embora outras línguas fazer!) Por isso, quando os genéricos foram introduzidos, foi tomada a decisão de que Java seria apenas apagar os tipos genéricos. Assim, em tempo de execução, não há nenhum problema digite seu código.
Vamos dar uma olhada no código compilado:
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: astore_2
10: aload_2
11: ldc #4 // String test
13: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
18: pop
19: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
22: aload_1
23: iconst_0
24: invokeinterface #7, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
29: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
32: return
Aqui você pode ver diretamente que não há nenhum tipo de verificações em tempo de execução. Então, a resposta completa (mas aparentemente irreverente) à sua pergunta é que Java só verifica os tipos em tempo de compilação com base nos tipos de variáveis (conhecido em tempo de compilação), mas parâmetros de tipo genérico são apagados e o código é executado sem eles.