Come vengono utilizzate le classi interne anonime in Java?


Risposte:


369

Per "classe anonima", suppongo che intendi classe interna anonima .

Una classe interna anonima può rivelarsi utile quando si crea un'istanza di un oggetto con determinati "extra" come metodi di sostituzione, senza dover effettivamente sottoclassare una classe.

Tendo a usarlo come scorciatoia per allegare un listener di eventi:

button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        // do something
    }
});

L'uso di questo metodo rende la codifica un po 'più veloce, poiché non ho bisogno di creare una classe aggiuntiva che implementa ActionListener- posso solo creare un'istanza di una classe interna anonima senza effettivamente creare una classe separata.

Uso questa tecnica solo per compiti "veloci e sporchi" in cui non è necessaria la creazione di un'intera classe. Avere più classi interne anonime che fanno esattamente la stessa cosa dovrebbe essere refactored in una classe reale, sia essa una classe interna o una classe separata.


5
Oppure potresti refactificare le classi interne anonime duplicate in un metodo con la classe interna anonima (e possibilmente qualche altro codice duplicato).
Tom Hawtin - tackline il

3
Ottima risposta ma una domanda veloce. Vuol dire che Java può vivere senza classi interne anonime e sono come un'opzione extra tra cui scegliere?
realPK,

5
Molto ben spiegato, anche duro suggerirei a chiunque legga questo di cercare e vedere cosa possono fare le espressioni java 8 e lambda per rendere la codifica più rapida e leggibile.
Pievis,

2
@utente2190639 Precisamente, non posso chiedere di meglio con Lambda in Java8
bonCodigo,

3
perché hai detto overloading methodse no overriding methods?
Tarun,

73

Le classi interne anonime sono effettivamente chiusure, quindi possono essere utilizzate per emulare espressioni lambda o "delegati". Ad esempio, prendi questa interfaccia:

public interface F<A, B> {
   B f(A a);
}

Puoi usarlo in forma anonima per creare una funzione di prima classe in Java. Supponiamo che tu abbia il seguente metodo che restituisce il primo numero più grande di i nell'elenco dato, oppure i se nessun numero è più grande:

public static int larger(final List<Integer> ns, final int i) {
  for (Integer n : ns)
     if (n > i)
        return n;
  return i;
}

E poi hai un altro metodo che restituisce il primo numero più piccolo di me nell'elenco dato, o i se nessun numero è più piccolo:

public static int smaller(final List<Integer> ns, final int i) {
   for (Integer n : ns)
      if (n < i)
         return n;
   return i;
}

Questi metodi sono quasi identici. Usando il tipo di funzione di prima classe F, possiamo riscriverli in un metodo come segue:

public static <T> T firstMatch(final List<T> ts, final F<T, Boolean> f, T z) {
   for (T t : ts)
      if (f.f(t))
         return t;
   return z;
}

È possibile utilizzare una classe anonima per utilizzare il metodo firstMatch:

F<Integer, Boolean> greaterThanTen = new F<Integer, Boolean> {
   Boolean f(final Integer n) {
      return n > 10;
   }
};
int moreThanMyFingersCanCount = firstMatch(xs, greaterThanTen, x);

Questo è un esempio davvero inventato, ma è facile vedere che essere in grado di trasmettere funzioni come se fossero valori è una caratteristica piuttosto utile. Vedi "Può il tuo linguaggio di programmazione farlo" di Joel stesso.

Una libreria piacevole per programmare Java in questo stile: Java funzionale.


20
sfortunatamente, la verbosità di fare la programmazione funzionale in Java, IMHO, supera i suoi guadagni - uno dei punti salienti della programmazione funzionale è che tende a ridurre le dimensioni del codice e rende le cose più facili da leggere e modificare. Ma Java funzionale non sembra farlo affatto.
Chii,

27
Tutta la comprensibilità della programmazione funzionale, con la dolcezza di Java!
Adam Jaskiewicz,

3
Nella mia esperienza, lo stile funzionale in Java è pagato con verbosità in anticipo, ma produce brevità a lungo termine. Ad esempio, myList.map (f) è notevolmente meno dettagliato del corrispondente for-loop.
Apocalisp,

2
Scala , un linguaggio in stile di programmazione funzionale, presumibilmente funziona bene all'interno della JVM e può essere un'opzione per scenari di programmazione funzionale.
Darrell Teague,

51

La classe interna anonima viene utilizzata nel seguente scenario:

1.) Per l'override (sottoclasse), quando la definizione della classe non è utilizzabile tranne il caso corrente:

class A{
   public void methodA() {
      System.out.println("methodA");
    }
}
class B{
    A a = new A() {
     public void methodA() {
        System.out.println("anonymous methodA");
     }
   };
}

2.) Per l'implementazione di un'interfaccia, quando l'implementazione dell'interfaccia è richiesta solo per il caso corrente:

interface interfaceA{
   public void methodA();
}
class B{
   interfaceA a = new interfaceA() {
     public void methodA() {
        System.out.println("anonymous methodA implementer");
     }
   };
}

3.) Argomento definito classe interna anonima:

 interface Foo {
   void methodFoo();
 }
 class B{
  void do(Foo f) { }
}

class A{
   void methodA() {
     B b = new B();
     b.do(new Foo() {
       public void methodFoo() {
         System.out.println("methodFoo");
       } 
     });
   } 
 } 

8
Ottima risposta, sembra 3.) è il modello utilizzato per gli ascoltatori di eventi
xdl

47

Li uso a volte come hack di sintassi per l'istanza di Map:

Map map = new HashMap() {{
   put("key", "value");
}};

vs

Map map = new HashMap();
map.put("key", "value");

Risparmia un po 'di ridondanza quando si fanno molte dichiarazioni put. Tuttavia, ho anche riscontrato problemi nel fare questo quando la classe esterna deve essere serializzata tramite telecomando.


56
Per essere chiari, il primo set di parentesi graffe è la classe interna anonima (sottoclasse HashMap). Il secondo set di parentesi graffe è un inizializzatore di istanza (anziché statico) che quindi imposta i valori nella sottoclasse di HashMap. +1 per averlo menzionato, -1 per non averlo scritto per i noob. ;-D
Spencer Kormos il

4
Maggiori informazioni sulla sintassi a doppia parentesi qui .
Martin Andersson,


18

Sono comunemente usati come una forma dettagliata di callback.

Suppongo che potresti dire che sono un vantaggio rispetto al non averli e alla necessità di creare una classe denominata ogni volta, ma concetti simili sono implementati molto meglio in altre lingue (come chiusure o blocchi)

Ecco un esempio swing

myButton.addActionListener(new ActionListener(){
    public void actionPerformed(ActionEvent e) {
        // do stuff here...
    }
});

Sebbene sia ancora molto prolisso, è molto meglio che costringerti a definire una classe nominata per ogni ascoltatore a eliminazione come questo (anche se a seconda della situazione e del riutilizzo, potrebbe essere comunque l'approccio migliore)


1
Intendevi dire conciso? Se fosse dettagliato, il callback rimarrebbe separatamente, rendendolo un po 'più grande e rendendolo così dettagliato. Se dici che questo è ancora dettagliato, quale sarebbe una forma concisa allora?
user3081519,

1
@ user3081519, qualcosa di simile myButton.addActionListener(e -> { /* do stuff here */})o myButton.addActionListener(stuff)sarebbe più difficile.
Samuel Edwin Ward,

8

Lo usi in situazioni in cui devi creare una classe per uno scopo specifico all'interno di un'altra funzione, ad es. Come ascoltatore, come eseguibile (per generare un thread), ecc.

L'idea è che li chiami dall'interno del codice di una funzione in modo da non farvi mai riferimento ad altrove, quindi non è necessario nominarli. Il compilatore li elenca appena.

Sono essenzialmente zucchero sintattico e dovrebbero generalmente essere spostati altrove man mano che crescono.

Non sono sicuro che sia uno dei vantaggi di Java, anche se se li usi (e sfortunatamente li usiamo tutti frequentemente), potresti argomentare che sono uno di questi.


6

Linee guida per la classe anonima.

  1. La classe anonima viene dichiarata e inizializzata contemporaneamente.

  2. La classe anonima deve essere estesa o implementata a una sola classe o interfaccia resp.

  3. Poiché la classe anonymouse non ha nome, può essere utilizzata una sola volta.

per esempio:

button.addActionListener(new ActionListener(){

            public void actionPerformed(ActionEvent arg0) {
        // TODO Auto-generated method stub

    }
});

Riguardo al n. 3: non del tutto vero. È possibile acquisire più istanze di una classe anonima con la riflessione, ad es ref.getClass().newInstance().
Icza,

Le regole non rispondono alla domanda.
Marchese di Lorne,

5

Sì, le classi interne anonime sono sicuramente uno dei vantaggi di Java.

Con una classe interna anonima hai accesso alle variabili finali e dei membri della classe circostante, e ciò risulta utile negli ascoltatori ecc.

Ma un grande vantaggio è che il codice della classe interna, che è (almeno dovrebbe essere) strettamente accoppiato alla classe / metodo / blocco circostante, ha un contesto specifico (classe, metodo e blocco circostanti).


1
Avere accesso alla classe circostante è molto importante! Penso che questo sia il motivo in molti casi in cui viene usata una classe anonima, perché ha bisogno / usa gli attributi, i metodi e le variabili locali non pubblici della classe / metodo circostante che esternamente (se si usasse una classe separata) dovrebbe essere superato o pubblicato.
Icza,

5
new Thread() {
        public void run() {
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                System.out.println("Exception message: " + e.getMessage());
                System.out.println("Exception cause: " + e.getCause());
            }
        }
    }.start();

Questo è anche uno degli esempi di tipo interno anonimo che utilizza thread


3

io uso oggetti anonimi per chiamare nuovi thread ..

new Thread(new Runnable() {
    public void run() {
        // you code
    }
}).start();

3

Una classe interna è associata a un'istanza della classe esterna e ci sono due tipi speciali: classe locale e classe anonima . Una classe anonima ci consente di dichiarare e creare un'istanza di una classe contemporaneamente, rendendo quindi il codice conciso. Li usiamo quando abbiamo bisogno di una classe locale solo una volta perché non hanno un nome.

Considera l'esempio di doc in cui abbiamo una Personclasse:

public class Person {

    public enum Sex {
        MALE, FEMALE
    }

    String name;
    LocalDate birthday;
    Sex gender;
    String emailAddress;

    public int getAge() {
        // ...
    }

    public void printPerson() {
        // ...
    }
}

e abbiamo un metodo per stampare membri che corrispondono ai criteri di ricerca come:

public static void printPersons(
    List<Person> roster, CheckPerson tester) {
    for (Person p : roster) {
        if (tester.test(p)) {
            p.printPerson();
        }
    }
}

dove si CheckPersontrova un'interfaccia come:

interface CheckPerson {
    boolean test(Person p);
}

Ora possiamo fare uso di una classe anonima che implementa questa interfaccia per specificare i criteri di ricerca come:

printPersons(
    roster,
    new CheckPerson() {
        public boolean test(Person p) {
            return p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25;
        }
    }
);

Qui l'interfaccia è molto semplice e la sintassi della classe anonima sembra ingombrante e poco chiara.

Java 8 ha introdotto un termine Interface funzionale che è un'interfaccia con un solo metodo astratto, quindi possiamo dire che CheckPersonè un'interfaccia funzionale. Possiamo usare Lambda Expression che ci consente di passare la funzione come argomento del metodo come:

printPersons(
                roster,
                (Person p) -> p.getGender() == Person.Sex.MALE
                        && p.getAge() >= 18
                        && p.getAge() <= 25
        );

Possiamo usare un'interfaccia funzionale standard Predicateal posto dell'interfaccia CheckPerson, che ridurrà ulteriormente la quantità di codice richiesto.


2

La classe interna anonima può essere utile mentre fornisce implementazioni diverse per oggetti diversi. Ma dovrebbe essere usato con parsimonia in quanto crea problemi per la leggibilità del programma.


1

Uno dei principali utilizzi delle classi anonime nella finalizzazione delle classi che chiamava finalizer guardian . Nel mondo Java l'uso dei metodi finalize dovrebbe essere evitato fino a quando non ne avrete davvero bisogno. Devi ricordare che, quando esegui l'override del metodo finalize per le sottoclassi, devi sempre invocaresuper.finalize() anche, poiché il metodo finalize della superclasse non invocherà automaticamente e potresti avere problemi con le perdite di memoria.

quindi considerando il fatto sopra menzionato, puoi semplicemente usare le classi anonime come:

public class HeavyClass{
    private final Object finalizerGuardian = new Object() {
        @Override
        protected void finalize() throws Throwable{
            //Finalize outer HeavyClass object
        }
    };
}

Usando questa tecnica hai sollevato te stesso e gli altri sviluppatori affinché invocassero super.finalize()ciascuna sottoclasse del HeavyClassmetodo che necessita di finalizzazione.


1

Puoi usare la classe anonima in questo modo

TreeSet treeSetObj = new TreeSet(new Comparator()
{
    public int compare(String i1,String i2)
    {
        return i2.compareTo(i1);
    }
});

1

Sembra che nessuno abbia menzionato qui, ma puoi anche usare una classe anonima per contenere argomenti di tipo generico (che normalmente perdono a causa della cancellazione del tipo) :

public abstract class TypeHolder<T> {
    private final Type type;

    public TypeReference() {
        // you may do do additional sanity checks here
        final Type superClass = getClass().getGenericSuperclass();
        this.type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
    }

    public final Type getType() {
        return this.type;
    }
}

Se istanzerai questa classe in modo anonimo

TypeHolder<List<String>, Map<Ineger, Long>> holder = 
    new TypeHolder<List<String>, Map<Ineger, Long>>() {};

allora tale holder istanza conterrà una definizione non cancellata di tipo passato.

uso

Questo è molto utile per la costruzione di validatori / deserializzatori. Inoltre puoi istanziare il tipo generico con la riflessione (quindi se hai mai voluto fare un new T()tipo parametrizzato, sei il benvenuto!) .

Svantaggi / Limiti

  1. Dovresti passare esplicitamente il parametro generico. In caso contrario, si avrà la perdita del parametro di tipo
  2. Ogni istanza ti costerà una classe aggiuntiva che verrà generata dal compilatore che porta all'inquinamento del percorso di classe / gonfiore del vaso

1

Il modo migliore per ottimizzare il codice. inoltre, possiamo usare per un metodo prioritario di una classe o interfaccia.

import java.util.Scanner;
abstract class AnonymousInner {
    abstract void sum();
}

class AnonymousInnerMain {
    public static void main(String []k){
        Scanner sn = new Scanner(System.in);
        System.out.println("Enter two vlaues");
            int a= Integer.parseInt(sn.nextLine());
            int b= Integer.parseInt(sn.nextLine()); 
        AnonymousInner ac = new AnonymousInner(){
            void sum(){
                int c= a+b;
                System.out.println("Sum of two number is: "+c);
            }
        };
        ac.sum();
    }

}

1

Una classe interna anonima viene utilizzata per creare un oggetto a cui non verrà mai più fatto riferimento. Non ha nome ed è dichiarato e creato nella stessa istruzione. Questo è usato dove normalmente useresti la variabile di un oggetto. Sostituisci la variabile con la newparola chiave, una chiamata a un costruttore e la definizione della classe all'interno {e }.

Quando si scrive un programma threaded in Java, di solito dovrebbe apparire così

ThreadClass task = new ThreadClass();
Thread runner = new Thread(task);
runner.start();

L' ThreadClassusato qui sarebbe definito dall'utente. Questa classe implementerà l' Runnableinterfaccia richiesta per la creazione di thread. Nel metodo deve essere implementato anche ThreadClassil run()metodo (solo metodo in Runnable). È chiaro che sbarazzarsi diThreadClass sarebbe più efficiente ed è esattamente per questo che esistono le classi interne anonime.

Guarda il seguente codice

Thread runner = new Thread(new Runnable() {
    public void run() {
        //Thread does it's work here
    }
});
runner.start();

Questo codice sostituisce il riferimento fatto tasknell'esempio più in alto. Anziché avere una classe separata, la Classe interna anonima all'interno del Thread()costruttore restituisce un oggetto senza nome che implementa l' Runnableinterfaccia e sovrascrive il run()metodo. Il metodo run()includerebbe istruzioni all'interno che svolgono il lavoro richiesto dal thread.

Rispondendo alla domanda se le classi interne anonime sono uno dei vantaggi di Java, dovrei dire che non sono del tutto sicuro poiché al momento non ho familiarità con molti linguaggi di programmazione. Ma quello che posso dire è che è sicuramente un metodo di codifica più rapido e semplice.

Riferimenti: Sams insegna a te stesso in Java in 21 giorni della settima edizione


0

Un altro vantaggio:
come sai che Java non supporta l'ereditarietà multipla, quindi se usi la classe kinda "Thread" come classe anonima, alla classe rimane ancora uno spazio per qualsiasi altra classe da estendere.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.