public class Test { // No throws clause here public static void main(String[] args) { doThrow(new SQLException()); } static void doThrow(Exception e) { Test.
doThrow0(e); } @SuppressWarnings("unchecked") static
void doThrow0(Exception e) throws E { throw (E) e; } }
class Test { Object x() { return "abc"; } String x() { return "123"; } }
Certo. A linguagem Java não permite que dois métodos sejam substituídos de forma equivalente dentro da mesma classe ao mesmo tempo, independentemente de suas diferenças em lançamentos ou tipos de retorno. Mas espere um minuto. Verifique a documentação novamente para Class.getMethod(String, Class…). Diz:
Observe que é possível que haja mais de um método correspondente em uma classe, porque enquanto a linguagem Java proíbe vários métodos com a mesma assinatura, mas tipos de retorno diferentes, a Java Virtual Machine não o faz. Essa flexibilidade em uma máquina virtual pode ser usada para implementar vários recursos de linguagem. Por exemplo, retornos covariantes podem ser feitos com métodos de ponte; O método bridge e o método substituído teriam a mesma assinatura, mas tipos de retorno diferentes. Uau, isso faz sentido. Na verdade, muita coisa acontece quando você escreve o seguinte: Observe o bytecode gerado: Então, t é na verdade um objeto no bytecode. Isto é bem compreendido. O método de ponte sintética é, na verdade, gerado pelo compilador porque o tipo de retorno Parent.x() pode ser esperado em certas partes das chamadas. Adicionar genéricos sem esses métodos de ponte não será mais possível na representação binária. Portanto, alterar a JVM para permitir tal recurso produziu menos problemas (o que também permite a substituição do método covariante como efeito colateral...) Inteligente, certo?
3. Todos os itens a seguir são matrizes bidimensionais. Isso é realmente verdade. Mesmo que o seu analisador mental não consiga entender imediatamente o tipo de retorno dos métodos descritos acima, eles são todos iguais! Como o próximo trecho de código. Você acha que isso é loucura? A quantidade de oportunidades para escrever também simplesmente explode a imaginação!
Anotação de tipo.
Um dispositivo cujo mistério perde apenas para seu poder. Ou em outras palavras: quando eu faço meu último commit pouco antes das minhas férias de 4 semanas. Dou-lhe permissão para usá-lo da maneira que desejar.
4. Você não receberá uma condicional Então você pensou que já sabia tudo sobre condicionais quando começou a usá-las? Deixe-me decepcioná-lo - você estava errado. A maioria de vocês pensará que os dois exemplos a seguir são equivalentes: equivalente a isto? Não. Vamos fazer um teste rápido. O programa irá gerar o seguinte: Sim! O operador condicional executará conversões de tipo, se necessário. Porque caso contrário você esperaria que o programa lançasse uma NullPointerException?
5. Você também não receberá um operador de atribuição composto. A desenvoltura é suficiente? Vejamos os dois trechos de código a seguir:
abstract class Parent
{ abstract T x(); } class Child extends Parent
{ @Override String x() { return "abc"; } }
// Method descriptor #15 ()Ljava/lang/String; // Stack: 1, Locals: 1 java.lang.String x(); 0 ldc
[16] 2 areturn Line numbers: [pc: 0, line: 7] Local variable table: [pc: 0, pc: 3] local: this index: 0 type: Child // Method descriptor #18 ()Ljava/lang/Object; // Stack: 1, Locals: 1 bridge synthetic java.lang.Object x(); 0 aload_0 [this] 1 invokevirtual Child.x() : java.lang.String [19] 4 areturn Line numbers: [pc: 0, line: 1]
class Test { int[][] a() { return new int[0][]; } int[] b() [] { return new int[0][]; } int c() [][] { return new int[0][]; } }
class Test { int[][] a = {{}}; int[] b[] = {{}}; int c[][] = {{}}; }
@Target(ElementType.TYPE_USE) @interface Crazy {} class Test { @Crazy int[][] a1 = {{}}; int @Crazy [][] a2 = {{}}; int[] @Crazy [] a3 = {{}}; @Crazy int[] b1[] = {{}}; int @Crazy [] b2[] = {{}}; int[] b3 @Crazy [] = {{}}; @Crazy int c1[][] = {{}}; int c2 @Crazy [][] = {{}}; int c3[] @Crazy [] = {{}}; }
Object o1 = true ? new Integer(1) : new Double(2.0);
Object o2; if (true) o2 = new Integer(1); else o2 = new Double(2.0);
System.out.println(o1); System.out.println(o2);
1.0 1
Integer i = new Integer(1); if (i.equals(1)) i = null; Double d = new Double(2.0); Object o = true ? i : d; // NullPointerException! System.out.println(o);
i += j; i = i + j;
Intuitivamente, eles deveriam ser iguais, certo? Mas você sabe o que - eles são diferentes. A especificação JLS diz:
Uma expressão composta do tipo E1 op = E2 é equivalente a E1 = (T) ((E1) op (E2)), onde T é o tipo de E1, exceto que E1 é avaliado apenas uma vez. Um bom exemplo é usar *= ou /= :
byte b = 10; b *= 5.7; System.out.println(b); // prints 57
or: or
byte b = 100; b /= 2.5; System.out.println(b); // prints 40
:
char ch = '0'; ch *= 1.1; System.out.println(ch); // prints '4'
or:
char ch = 'A'; ch *= 1.5; System.out.println(ch); // prints 'a'
Então, essa ainda é uma ferramenta útil?
6. Números inteiros aleatórios Agora, uma tarefa mais difícil. Não leia a solução. Veja se você consegue encontrar a resposta sozinho. Quando executo o seguinte programa:
for (int i = 0; i < 10; i++) { System.out.println((Integer) i); }
Às vezes recebo a seguinte saída:
92 221 45 48 236 183 39 193 33 84
Mas como isso é possível? Ok, a resposta está em substituir o cache inteiro do JDK por meio de reflexão e, em seguida, usar o boxe e o unboxing automáticos. Não faça isso sem permissão de um adulto! Ou seja:
7. GOTO Um dos meus favoritos. Java tem GOTO! Escreva isto:
int goto = 1;
e você verá isto: Isso porque goto é uma palavra reservada não utilizada, só para garantir... Mas essa não é a parte interessante. O legal é que você pode incluir goto emparelhado com break, continue e blocos marcados: Saltando para frente No código de bytes: Saltando para trás No código de bytes:
8. Java tem aliases de tipo Em outras linguagens (como Ceilão), podemos definir tipo aliases muito fáceis: a classe People aqui é construída de tal forma que pode ser trocada por um Set
Test.java:44: error:
expected int goto = 1; ^
label: { // do stuff if (check) break label; // do more stuff }
2 iload_1 [check] 3 ifeq 6 // Jumping forward 6 ..
label: do { // do stuff if (check) continue label; // do more stuff break label; } while(true);
2 iload_1 [check] 3 ifeq 9 6 goto 2 // Jumping backward 9 ..
interface People => Set
;
People? p1 = null; Set
? p2 = p1; People? p3 = p2;
class Test {
void x(I i, L l) { System.out.println( i.intValue() + ", " + l.longValue() ); } }
new Test().x(1, 2L);
// A helper type. You could also just use List interface Type
{} class C implements Type
> {} class D
implements Type
Então, o que C e D significam? De certa forma, eles são recursivos, semelhantes à recursão em java.lang.Enum. Considere: Dadas as especificações acima, a implementação real de enum é apenas um açúcar sintático: com isso em mente, vamos retornar aos nossos dois tipos. O código a seguir será compilado? Uma questão difícil... e que não está realmente sendo resolvida? C é um subtipo de Type ? Tente compilar isso no seu Eclipse ou Idea e eles dirão o que pensam. Jogue no ralo... Alguns relacionamentos de tipo em Java são indecidíveis! 10. Interseção de tipos Java possui um recurso muito interessante chamado interseção de tipos. Você pode declarar um tipo (genérico) que é na verdade a interseção de dois tipos. Por exemplo: O parâmetro de tipo customizado T associado às instâncias da classe Test deve incluir as interfaces Serializable e Cloneable. Por exemplo, String não pode ser restrito, mas Date pode: Este recurso tem vários usos em Java8, onde você pode converter tipos. Como isso ajuda? Quase nada, mas se você quiser converter sua expressão lambda no tipo que você precisa, não há outra maneira. Digamos que você tenha uma limitação tão maluca no seu método: Você quer Runnable que ao mesmo tempo seja Serializable apenas se quiser executá-lo em outro lugar e enviar o resultado pela rede. Lambda e serialização adicionam um pouco de ironia. Você pode serializar sua expressão lambda se o tipo de destino e os argumentos forem serializáveis. Mas mesmo que isso seja verdade, eles não habilitam automaticamente a interface Serializable. Você mesmo deve trazê-los para esse tipo. Mas quando você converte apenas para Serializable: então o lambda não será mais Runnable, então converta-os para os dois tipos: E para concluir: public abstract class Enum
// This enum MyEnum {} // Is really just sugar for this class MyEnum extends Enum
class Test { Type c = new C(); Type> d = new D
Step 0) C Step 1) Type
>> > Step 4) D
class Test
// Doesn't compile Test
execute((Serializable) (() -> {}));
execute((Runnable & Serializable) (() -> {}));
GO TO FULL VERSION