2015-10-05 38 views
36

ho imparato l'altro giorno che si può fare questoInvocare un metodo di una classe anonima

new Object() { 
    void hello() { 
     System.out.println("Hello World!"); 
    } 
}.hello(); 

Questo sembra davvero strano per me. Sicuramente il tipo statico dell'oggetto creato è Object, quindi non esiste un metodo hello()? Non è quasi del tutto inutile (non è possibile invocare due volte lo hello).

Ho 2 domande a riguardo.

  1. Qualcuno può indicarmi la parte della specifica che risolve questo problema?
  2. Ho ragione nel pensare che l'unico modo per invocare hello è immediatamente simile a questo. Che dire della riflessione?

Grazie

+0

[JLS-15.9.5. Dichiarazioni di classe anonime] (http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.9.5) –

+0

Dovresti riuscire ad arrivarci tramite la riflessione. Ma sì, questo è quasi completamente inutile.Se si desidera chiamare metodi, di solito si implementa un'interfaccia nella classe anonima. – Thilo

+4

Bene, se si crea una classe anonima di un tipo più specifico (come un'implementazione anonima di * un'interfaccia *), è possibile conservare il riferimento in una variabile che consente di chiamare tali metodi a piacimento. –

risposta

16

Qualcuno può indicarmi la parte della specifica che risolve questo problema?

Questo sarà principalmente essere definito nella sezione relativa Method invocation expressions:

Il primo passo nella elaborazione di una chiamata di metodo in fase di compilazione è figura il nome del metodo da richiamare e quale classe oppure interfaccia per cercare le definizioni dei metodi di quel nome.

Per la classe o l'interfaccia per la ricerca, ci sono sei casi da considerare, a seconda della forma che precede la parentesi sinistra del MethodInvocation:

  • [...]
  • Se il modulo è Primary . [TypeArguments] Identifier, quindi lasciare T essere il tipo di espressione primaria. La classe o l'interfaccia per cercare è T se T è una classe o un tipo di interfaccia o il limite superiore di T se T è una variabile di tipo.

Qui, l'espressione primaria è il class instance creation expression. Quindi il tipo da cercare è il tipo anonimo.

Ho ragione nel pensare che l'unico modo per invocare ciao è immediatamente simile a questo. Che dire della riflessione?

Finché espressione restituisce il tipo anonimo T, sia attraverso l'accesso diretto come voi avere, o tramite farmaci generici, si ha accesso (a regole di accesso regolari) ai membri che T dichiara. Questo non è limitato ai metodi. Puoi accedere a campi o tipi, anche se non è così utile per i tipi. Ad esempio,

Object var = new Object() { 
    class Nested { 
    } 
}.new Nested(); 

Dal momento che non c'è modo di fare riferimento al tipo nidificato senza il tipo di inclusione, non è possibile dichiarare una variabile di quel tipo nidificato. L'utilità diminuisce molto rapidamente. (Presumibilmente, questo è anche il motivo per cui non è possibile avere un tipo annidato static all'interno di questa classe anonima.)

Reflection espone anche questo metodo. La classe anonima generata contiene questo metodo, quindi è possibile recuperarlo e richiamarlo. Il processo è lo stesso. Il fatto che l'istanza provenga da una classe anonima non ha importanza. Si applica la stessa strategia presentata in How do I invoke a Java method when given the method name as a string?.

Per esempio,

Object ref = new Object() { 
    public void method() { 
     System.out.println("hidden"); 
    } 
}; 
Class<?> anonymousClass = ref.getClass(); 
Method method = anonymousClass.getMethod("method"); 
method.invoke(ref, new Object[0]); 

Non bisogna mai scrivere codice come questo.

+2

@ElliottFrisch Questo non dovrebbe essere il caso. 'new Object() {} .getClass()' restituisce 'class com.example.Example $ 1' per me. –

+0

Oracle Java 8, Linux. 'o.getClass(). getMethods()' non ha prodotto un metodo 'ciao'. È lì che mi sono fermato. –

+4

@elliot Se hai usato il loro codice, non funzionerà perché il metodo non è pubblico. –

12

quanto pubblicato, non c'è un modo per ottenere i metodi anonimi dall'istanza Object. E fa sembrare inutile lo Anonymous classes. Ma potresti (e di solito lo farei) usarlo per implementare un'interfaccia. Qualcosa di simile,

static interface Hello { 
    void hello(); 
} 
public static void main(String[] args) { 
    Hello o = new Hello() { 
     public void hello() { 
      System.out.println("Hello World!"); 
     } 
    }; 
    o.hello(); 
} 

o, più comunemente, per i call-back con JFC/Swing e ActionListener.

11

Aggiungendo a Sotirios' answer, ecco come per richiamare il metodo attraverso la riflessione:

Object o = new Object() { 
    void hello() { 
     System.out.println("Hello World!"); 
    } 
}; 

Method hello = o.getClass().getDeclaredMethod("hello"); 
hello.invoke(o); 

questo modo è possibile chiamare il metodo più di una volta, ma a parte questo, ha poco senso.

+0

Grazie. Sono d'accordo che è necessario per una risposta completa. –

9

classe anonima è a beneficio di programmatori pigri - nominare le cose è troppo difficile :)

classe anonima è molto simile a una classe locale. Se una classe locale è solo per creare un oggetto, che viene poi usato solo come supertipo, possiamo invece creare una classe anonima che è più succinta.

La classe anonima non è idonea (al programmatore), il che va bene perché non è necessario fare nuovamente riferimento. Tuttavia, per il compilatore, la classe ha un nome molto grande e non vi è alcun motivo per trattarla in modo diverso rispetto alle classi con un nome esplicito. Il tipo statico dell'espressione è la sottoclasse concreta e i membri dell'oggetto sono i membri di quella classe. Questa funzione (essere in grado di chiamare hello()) è inutile? Solo se consideri la classe locale inutile (e in effetti la classe locale è usata raramente). Ecco uno example in cui ho usato quella funzione (per divertimento).

Sebbene il tipo sia irrilevante, il tipo può sopravvivere come se stesso tramite le API. Ad esempio,

Objects.requireNonNull(new Object(){ void hello(){} }).hello(); 

Anche se non possiamo nominare il tipo, non ha bisogno di essere chiamato in cui si può dedurre.

Collections.singleton(new Object(){ void hello(){} }) 
     .forEach(o->{ o.hello(); o.hello(); }); 

E possiamo creare puzzlers per le persone che non si aspettavano il tipo statico

Collections.singleton(new Object(){}).add(new Object()); // does not compile! why? 
+1

Quindi la mia premessa è completamente sbagliata - puoi chiamare 'ciao()' due volte senza riflessione. Questo è bello, ma anche un po 'ridicolo. Ispirato dalla tua risposta, l'ho gestito con 'Collections.nCopies (2, new Object() {...}). ForEach (o -> o.hello());' –

+2

sì, la tua è una dimostrazione migliore. – ZhongYu

+1

Mentre il programmatore non può assegnare un nome al tipo concreto dell'espressione, java fornisce anche molti punti in cui non è necessario dare un nome al tipo, ad es. parametro lambda. – ZhongYu

Problemi correlati