2012-11-29 6 views
6

Se si dispone di una classe astratta, è possibile creare un'istanza derivante da una classe anonima concreta. Questo è un esempio:Come istanziare anonimamente una classe astratta memorizzata in un oggetto Class in Java?

abstract class A { 
    abstract void hello(); 
} 

A say = new A() { void hello() { System.out.println ("hello"); } } 

say.hello(); // -> hello 

Come fare lo stesso se la classe è memorizzata in un oggetto Classe? Ecco un esempio:

// -*- compile-command: "javac anon.java && java anon"; -*- 

class anon 
{ 
    anon() throws Exception {} 

    abstract class AbstractClass 
    { 
     AbstractClass() throws Exception {} 
     abstract void id(); 
    } 

    AbstractClass x = new AbstractClass() 
     { 
      void id() { System.out.println ("X"); } 
     }; 

    Class<AbstractClass> abstractclass 
     = (Class<AbstractClass>)Class.forName ("anon$AbstractClass"); 

    AbstractClass y = abstractclass.getConstructor().newInstance(); 

    public static void main (String argv[]) throws Exception 
    { 
     anon main = new anon(); 
     main.x.id(); // should print "X" 
     main.y.id(); // should print "Y" 
    } 
} 

Il primo instanziazione (x) funziona bene, ma il secondo (y) non riesce perché cerca di creare un'istanza della classe astratta direttamente senza derivare una classe concreta. Come posso fare questo in Java avendo solo un oggetto di classe?

+0

Attendere. Hai provato a implementare la seconda via con il primo codice? Penso che non funzionerà anche lì. Perché stai provando a creare un'istanza di una classe astratta come quella? Quando crei una classe anonima, stai effettivamente estendendo la tua classe astratta e istanzializzandola, e non la stessa classe astratta. –

+0

No y.id() dovrebbe stampare "Y". – ceving

+0

@ceving .. Hai capito il mio commento? –

risposta

5

Si può avere un malinteso su come funzionano esattamente le classi anonime. Una classe anonima è in effetti una classe regolare come qualsiasi altra e ha un proprio file di classe. Java-the-language fornisce solo un po 'di zucchero sintattico e consente una sintassi meno dettagliata per qualcosa che si può esattamente imitare dichiarando una normale classe di primo livello nel proprio file. Questo è il motivo per cui l'API Reflection è inutile per ciò che si desidera ottenere. Fondamentalmente, vuoi creare dinamicamente una classe che non ha il suo file di classe. Per questo è necessaria una libreria adatta, come ad esempio javassist.

+0

Peccato. Ogni volta che devo scrivere Java devo passare molto tempo per eludere i limiti di Java. Ma questa volta il muro sembra una parete rocciosa. – ceving

+1

Abbastanza vero. Il confronto di Java con una roccia è in realtà un'analogia molto fertile e funziona in molti casi distinti. Mi piace immaginare l'argilla per i linguaggi dinamici come ruby ​​e JavaScript, al contrario dei lego di Java. Si bloccano solo in alcuni modi ristretti e predefiniti. –

+0

Hmmm Riesco a ricordarmi di aver slappato alcuni mattoncini Lego per ottenere qualcosa da costruire. Deve essere geneticamente determinato che ho questi problemi. – ceving

1

Se A sarebbe un'interfaccia invece di una classe astratta, è possibile farlo con un proxy dinamico, ma ciò non funziona con una classe astratta. Esempio di come funziona con un'interfaccia:

import java.lang.reflect.InvocationHandler; 
import java.lang.reflect.Method; 
import java.lang.reflect.Proxy; 

interface A { 
    void hello(); 
} 

public class Example { 
    public static void main(String[] args) throws Exception { 
     @SuppressWarnings("unchecked") 
     Class<A> cls = (Class<A>) Class.forName("A"); 

     InvocationHandler handler = new InvocationHandler() { 
      @Override 
      public Object invoke(Object proxy, Method method, Object[] args) 
        throws Throwable { 
       System.out.println(method.getName()); 
       return null; 
      } 
     }; 

     A instance = (A) Proxy.newProxyInstance(cls.getClassLoader(), 
      new Class<?>[] { cls }, handler); 

     instance.hello(); 
    } 
} 
+0

Hmmm suona come una nuova e divertente limitazione Java di cui non sono ancora a conoscenza: qualcosa che può essere fatto con le interfacce ma non con le classi astratte anche se sono quasi le stesse. – ceving

0

Le classi astratte non possono essere istanziate, quindi è effettivamente necessaria una nuova classe concreta che estenda la classe astratta. Le classi sono prodotte dal compilatore java dal codice sorgente. Quindi scrivi quel codice sorgente ed esegui il compilatore java. Questo non è facile da fare dinamicamente, poiché il compilatore java richiede che il codice sorgente risieda in un file e mette anche le classi compilate nel file system, ma è possibile. Guarda come dovrebbe fare a Generating Java classes dynamically. Quindi devi caricare classi compilate, che è un'altra storia.

Se si considera questo come una "limitazione java", probabilmente hai scelto una lingua sbagliata per il tuo compito (o hai scelto un'attività sbagliata). Prova le lingue dinamiche basate su JVM: Groovy, JRuby ... ce ne sono molte.

+0

Sei libero di scegliere una lingua? Sei una cosa fortunata! – ceving

+0

Puoi usare java ovunque, ma chiama groovy da java proprio per questo compito. –

0

Come indicato da Marko, una classe anonima è uguale a qualsiasi altra a livello di file e byte. È solo zucchero sintattico a livello linguistico che rende facile scrivere in classi piccole.

Nell'esempio, x.getClass() non è una classe abstract. È una sottoclasse di AbstractClass, che con la definizione di id() non è più abstract. Probabilmente ha un nome come anon$1.

Naturalmente, se fosse astratto, non è possibile istanziarlo. Questo è esattamente quello che stai cercando di fare nell'assegnazione di . Il tuo riflesso è equivalente a y = anon.AbstractClass(); con override id(). Il riflesso è un errore in fase di esecuzione proprio come quella dichiarazione sarebbe un errore in fase di compilazione.

Di seguito probabilmente (a seconda l'esistenza di altre classi anonime, e il loro ordine) ed eseguire senza errori, ma stampa "X":

Class<AbstractClass> abstractclass 
    = (Class<AbstractClass>)Class.forName("anon$1"); // Note the different class name 
AbstractClass y = abstractclass.getConstructor().newInstance(); 
y.id(); // prints "X", not "Y" 

A quel punto ...

main.y.id(); // should print "Y" 

Da nessuna parte nel codice Hai un linea che stampa un carattere "Y", quindi non ci dovrebbe essere alcun motivo di aspettarsi esso.

+0

Era esattamente la domanda su come scrivere la riga di stampa "Y". – ceving

+0

Nessun punto del codice: 'System.out.println (" Y ");' esiste. Il tuo commento (nel codice) non è valido. – Anm

Problemi correlati