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"; } }
Giusto. Il linguaggio Java non consente l'override simultaneo di due metodi all'interno della stessa classe, indipendentemente dalle differenze nei lanci o nei tipi restituiti. Ma aspetta un attimo. Controlla di nuovo la documentazione per Class.getMethod(String, Class…). Dice:
Nota che è possibile che ci sia più di un metodo corrispondente in una classe, perché mentre il linguaggio Java proibisce più metodi con la stessa firma ma tipi di ritorno diversi, la Java Virtual Machine no. Questa flessibilità in una macchina virtuale può essere utilizzata per implementare varie funzionalità linguistiche. Ad esempio, i rendimenti covarianti possono essere ottenuti con i metodi bridge; Il metodo bridge e il metodo sottoposto a override avrebbero la stessa firma ma tipi di restituzione diversi. Wow, ha senso. In realtà c'è molto da fare quando scrivi quanto segue: Guarda il bytecode generato: Quindi t è effettivamente un oggetto nel bytecode. Questo è ben compreso. Il metodo bridge sintetico viene effettivamente generato dal compilatore perché è possibile prevedere il tipo restituito di Parent.x() in alcune parti delle chiamate. L'aggiunta di farmaci generici senza tali metodi bridge non sarà più possibile nella rappresentazione binaria. Quindi, modificare la JVM per consentire tale funzionalità ha prodotto meno problemi (il che consente anche l'override del metodo covariante come effetto collaterale...) Intelligente, giusto?
3. Tutti i seguenti sono array bidimensionali. Questo è effettivamente vero. Anche se il tuo analizzatore mentale non riesce a capire immediatamente il tipo di ritorno dei metodi sopra descritti, sono tutti uguali! Come il prossimo pezzo di codice. Pensi che sia pazzesco? Anche il numero di opportunità per scrivere fa esplodere semplicemente l'immaginazione!
Digitare l'annotazione.
Un dispositivo il cui mistero è secondo solo alla sua potenza. O in altre parole: quando prendo il mio ultimo impegno poco prima delle mie 4 settimane di vacanza. Ti do il permesso di usarlo nel modo che preferisci.
4. Non riceverai il condizionale Quindi, pensavi di sapere già tutto sui condizionali quando hai iniziato a usarli? Permettimi di deluderti: ti sbagliavi. La maggior parte di voi penserà che i due esempi seguenti siano equivalenti: equivalente a questo? NO. Usiamo un test veloce. Il programma restituirà quanto segue: Sì! L'operatore condizionale eseguirà i cast del tipo, se necessario. Perché altrimenti ti aspetteresti che il programma lanci una NullPointerException?
5. Inoltre non otterrai un operatore di assegnazione composto. L'intraprendenza è sufficiente? Diamo un'occhiata ai seguenti due frammenti di codice:
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 dovrebbero essere uguali, giusto? Ma sai una cosa: sono diversi. La specifica JLS dice:
Un'espressione composta di tipo E1 op = E2 è equivalente a E1 = (T) ((E1) op (E2)), dove T è il tipo di E1, tranne per il fatto che E1 viene valutato solo una volta. Un buon esempio è usare *= or /= :
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'
Quindi, è ancora uno strumento utile?
6. Interi casuali Ora passiamo a un compito più difficile. Non leggere la soluzione. Vedi se riesci a trovare la risposta da solo. Quando eseguo il seguente programma:
for (int i = 0; i < 10; i++) { System.out.println((Integer) i); }
A volte ottengo il seguente output:
92 221 45 48 236 183 39 193 33 84
Ma come è possibile? Ok, la risposta sta nell'override della cache dei numeri interi di JDK tramite riflessione e quindi nell'utilizzo dell'auto-boxing e dell'auto-unboxing. Non farlo senza il permesso di un adulto! O in altre parole:
7. GOTO Uno dei miei preferiti. Java ha GOTO! Scrivi questo:
int goto = 1;
e ottieni questo: Questo perché goto è una parola riservata inutilizzata, per ogni evenienza... Ma non è questa la parte emozionante. La cosa interessante è che puoi includere goto abbinato a break, continue e blocchi contrassegnati: Salto in avanti Nel byte code: Salto all'indietro Nel byte code:
8. Java ha alias di tipo In altri linguaggi (come Ceylon), possiamo definire il tipo alias molto semplici: la classe People qui è costruita in modo tale da poter essere scambiata con una 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
Allora cosa significano C e D? In un certo senso sono ricorsivi, simili alla ricorsione in java.lang.Enum. Considera: date le specifiche di cui sopra, l'effettiva implementazione di enum è solo zucchero sintattico: con questo in mente, torniamo ai nostri due tipi. Il seguente codice verrà compilato? Una domanda difficile... e in realtà non è stata risolta? C è un sottotipo di Type ? Prova a compilarlo nel tuo Eclipse o Idea e ti diranno cosa pensano. Buttatelo nello scarico... Alcune relazioni di tipo in Java sono indecidibili! 10. Intersezione dei tipi Java ha una caratteristica molto interessante chiamata intersezione dei tipi. Puoi dichiarare un tipo (generico) che è in realtà l'intersezione di due tipi. Ad esempio: il parametro di tipo personalizzato T associato alle istanze della classe Test deve includere entrambe le interfacce Serializable e Cloneable. Ad esempio, String non può essere limitato, ma Date sì: questa funzionalità ha molteplici usi in Java8, dove è possibile eseguire il cast dei tipi. In che modo questo aiuta? Quasi nulla, ma se vuoi trasformare la tua espressione lambda nel tipo che ti serve, non c'è altro modo. Diciamo che hai una limitazione così folle nel tuo metodo: vuoi Runnable che allo stesso tempo è serializzabile solo se vuoi eseguirlo in un altro posto e inviare il risultato in rete. Lambda e serializzazione aggiungono un po' di ironia. Puoi serializzare l'espressione lambda se il tipo di destinazione e gli argomenti sono serializzabili. Ma anche se ciò fosse vero, non abilitano automaticamente l'interfaccia Serializable. Devi portarli tu stesso a questo tipo. Ma quando esegui il cast solo su Serializable: allora lambda non sarà più Runnable, quindi lanciali su entrambi i tipi: E in conclusione: 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