Come posso scrivere una funzione anonima in Java?


87

È anche possibile?


6
Nota che questo è ora possibile in Java 8: vedi la risposta sulle espressioni Lambda di Mark Rotteveel di seguito.
Josiah Yoder

Risposte:


81

se intendi una funzione anonima e stai utilizzando una versione di Java precedente a Java 8, in una parola, no. ( Leggi le espressioni lambda se usi Java 8+ )

Tuttavia, puoi implementare un'interfaccia con una funzione come questa:

Comparator<String> c = new Comparator<String>() {
    int compare(String s, String s2) { ... }
};

e puoi usarlo con le classi interne per ottenere una funzione quasi anonima :)


6
Non ancora. In Java 7 che sta per essere possibile: stackoverflow.com/questions/233579/closures-in-java-7
Ilya Boyandin

2
Nel frattempo, in attesa di JDK7, i metodi anonimi possono essere emulati in un contesto OO utilizzando en.wikipedia.org/wiki/Command_pattern
gpampara

1
chiuso non è arrivato in Java 7.
Thorbjørn Ravn Andersen

5
Penso che dovresti modificare la tua risposta poiché abbiamo una funzione anonima con Java 8.
Node.JS

45

Ecco un esempio di una classe interna anonima.

System.out.println(new Object() {
    @Override public String toString() {
        return "Hello world!";
    }
}); // prints "Hello world!"

Questo non è molto utile così com'è, ma mostra come creare un'istanza di una classe interna anonima extends Objecte il @Overridesuo toString()metodo.

Guarda anche


Le classi interne anonime sono molto utili quando è necessario implementare una interfaceclasse che potrebbe non essere altamente riutilizzabile (e quindi non vale la pena refactoring alla propria classe denominata). Un esempio istruttivo sta usando un'usanzajava.util.Comparator<T> per l'ordinamento.

Ecco un esempio di come puoi ordinare un String[]basato su String.length().

import java.util.*;
//...

String[] arr = { "xxx", "cd", "ab", "z" };
Arrays.sort(arr, new Comparator<String>() {
    @Override public int compare(String s1, String s2) {
        return s1.length() - s2.length();
    }           
});
System.out.println(Arrays.toString(arr));
// prints "[z, cd, ab, xxx]"

Nota il trucco del confronto per sottrazione usato qui. Va detto che questa tecnica in generale è rotta: è applicabile solo quando si può garantire che non trabocchi (come nel caso delle Stringlunghezze).

Guarda anche


5
La maggior parte delle altre occorrenze può essere trovata come EventListener(sotto) implementazioni nell'applicazione Swing media.
BalusC

@BalusC: aggiunto collegamento alla domanda "come vengono usati"
polygenelubricants

@BalusC: stackoverflow ha recentemente aggiunto la Linkedbarra laterale, quindi sto facendo del mio meglio per utilizzarla.
polygenelubricants

12

Con l'introduzione dell'espressione lambda in Java 8 ora puoi avere metodi anonimi.

Diciamo che ho un corso Alphae voglio filtrare Alphai messaggi in base a una condizione specifica. Per fare questo puoi usare un file Predicate<Alpha>. Questa è un'interfaccia funzionale che ha un metodo testche accetta Alphae restituisce a boolean.

Supponendo che il metodo di filtro abbia questa firma:

List<Alpha> filter(Predicate<Alpha> filterPredicate)

Con la vecchia soluzione di classe anonima avresti bisogno di qualcosa del tipo:

filter(new Predicate<Alpha>() {
   boolean test(Alpha alpha) {
      return alpha.centauri > 1;
   }
});

Con le lambda Java 8 puoi fare:

filter(alpha -> alpha.centauri > 1);

Per informazioni più dettagliate, consulta il tutorial Lambda Expressions


2
Anche i riferimenti ai metodi sono utili. es. sort (String :: compareToIgnoreCase) docs.oracle.com/javase/tutorial/java/javaOO/…
Josiah Yoder

9

Classi interne anonime che implementano o estendono l'interfaccia di un tipo esistente sono state eseguite in altre risposte, anche se vale la pena notare che possono essere implementati più metodi (spesso con eventi in stile JavaBean, per esempio).

Una caratteristica poco riconosciuta è che sebbene le classi interne anonime non abbiano un nome, hanno un tipo. Nuovi metodi possono essere aggiunti all'interfaccia. Questi metodi possono essere richiamati solo in casi limitati. Principalmente direttamente newsull'espressione stessa e all'interno della classe (inclusi gli inizializzatori di istanze). Potrebbe confondere i principianti, ma può essere "interessante" per la ricorsione.

private static String pretty(Node node) {
    return "Node: " + new Object() {
        String print(Node cur) {
            return cur.isTerminal() ?
                cur.name() :
                ("("+print(cur.left())+":"+print(cur.right())+")");
        }
    }.print(node);
}

(L'ho scritto originariamente utilizzando nodepiuttosto che curnel printmetodo. Di ' NO per catturare "implicitamente final" i locali? )


nodedovrebbe essere dichiarato finalqui.
BalusC

@BalusC Bella cattura. In realtà il mio errore è stato quello di non usare cur.
Tom Hawtin - tackline

@ Tom: +1 bella tecnica! Viene effettivamente utilizzato ovunque nella pratica? Qualche nome per questo modello specifico?
polygenelubricants

@polygenelubricants Non per quanto ne so. Costa un intero oggetto extra! (E una classe.) Più o meno lo stesso valeva per l'idioma della doppia coppia. Le persone che pensano correttamente non sembrano preoccuparsi dell'idioma di Execute Around.
Tom Hawtin - tackline

@polygenelubricants In realtà non mi sembrano molti algoritmi ricorsivi (autonomi). In particolare quelli che non sono ricorsivi in ​​coda (o che lo sono facilmente) e non possono essere implementati chiamando il metodo pubblico (si noti il ​​leggermente irrilevante "Node" +per rendere necessario un secondo metodo). / Non ho un nome. Forse potrei creare una domanda di denominazione "sondaggio" (CW) e farla cadere nell'oblio.
Tom Hawtin - tackline

1

Sì, se stai usando l'ultima java che è la versione 8. Java8 rende possibile definire funzioni anonime che era impossibile nelle versioni precedenti.

Prendiamo esempio dai documenti java per sapere come dichiarare funzioni, classi anonime

L'esempio seguente, HelloWorldAnonymousClasses, utilizza classi anonime nelle istruzioni di inizializzazione delle variabili locali frenchGreeting e spanishGreeting, ma utilizza una classe locale per l'inizializzazione della variabile englishGreeting:

public class HelloWorldAnonymousClasses {

    interface HelloWorld {
        public void greet();
        public void greetSomeone(String someone);
    }

    public void sayHello() {

        class EnglishGreeting implements HelloWorld {
            String name = "world";
            public void greet() {
                greetSomeone("world");
            }
            public void greetSomeone(String someone) {
                name = someone;
                System.out.println("Hello " + name);
            }
        }

        HelloWorld englishGreeting = new EnglishGreeting();

        HelloWorld frenchGreeting = new HelloWorld() {
            String name = "tout le monde";
            public void greet() {
                greetSomeone("tout le monde");
            }
            public void greetSomeone(String someone) {
                name = someone;
                System.out.println("Salut " + name);
            }
        };

        HelloWorld spanishGreeting = new HelloWorld() {
            String name = "mundo";
            public void greet() {
                greetSomeone("mundo");
            }
            public void greetSomeone(String someone) {
                name = someone;
                System.out.println("Hola, " + name);
            }
        };
        englishGreeting.greet();
        frenchGreeting.greetSomeone("Fred");
        spanishGreeting.greet();
    }

    public static void main(String... args) {
        HelloWorldAnonymousClasses myApp =
            new HelloWorldAnonymousClasses();
        myApp.sayHello();
    }            
}

Sintassi delle classi anonime

Considera l'istanza dell'oggetto frenchGreeting:

    HelloWorld frenchGreeting = new HelloWorld() {
        String name = "tout le monde";
        public void greet() {
            greetSomeone("tout le monde");
        }
        public void greetSomeone(String someone) {
            name = someone;
            System.out.println("Salut " + name);
        }
    };

L'espressione di classe anonima è composta da quanto segue:

  • L' newoperatore
  • Il nome di un'interfaccia da implementare o di una classe da estendere. In questo esempio, la classe anonima implementa l'interfaccia HelloWorld.

  • Parentesi che contengono gli argomenti di un costruttore, proprio come una normale espressione di creazione di istanze di classe. Nota: quando si implementa un'interfaccia, non è presente alcun costruttore, quindi si utilizza una coppia di parentesi vuota, come in questo esempio.

  • Un corpo, che è un corpo di dichiarazione di classe. Più specificamente, nel corpo, le dichiarazioni di metodo sono consentite ma le istruzioni no.

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.