Jak mogę napisać anonimową funkcję w Javie?

87

Czy to w ogóle możliwe?

aarona
źródło
6
Zauważ, że jest to teraz możliwe w Javie 8 - zobacz odpowiedź na temat wyrażeń lambda autorstwa Marka Rotteveela poniżej.
Josiah Yoder

Odpowiedzi:

81

jeśli masz na myśli funkcję anonimową i używasz wersji Javy wcześniejszej niż Java 8, to jednym słowem nie. ( Przeczytaj o wyrażeniach lambda, jeśli używasz Java 8+ )

Możesz jednak zaimplementować interfejs z taką funkcją:

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

i możesz użyć tego z klasami wewnętrznymi, aby uzyskać prawie anonimową funkcję :)

chris
źródło
6
Jeszcze nie. W Javie 7 będzie to możliwe: stackoverflow.com/questions/233579/closures-in-java-7
Ilya Boyandin
2
W międzyczasie, podczas oczekiwania na JDK7, anonimowe metody mogą być emulowane w kontekście OO za pomocą en.wikipedia.org/wiki/Command_pattern
gpampara
1
Zamknięcie nie dotarło do Java 7.
Thorbjørn Ravn Andersen
5
Myślę, że należy zmodyfikować swoją odpowiedź, jak mamy anonimową funkcję z Java 8.
node.js
45

Oto przykład anonimowej klasy wewnętrznej.

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

Nie jest to zbyt użyteczne, ale pokazuje, jak utworzyć instancję anonimowej klasy wewnętrznej that extends Objecti @Overridejej toString()metodę.

Zobacz też


Anonimowe klasy wewnętrzne są bardzo przydatne, gdy trzeba zaimplementować klasę, interfacektóra może nie nadawać się do wielokrotnego użytku (i dlatego nie jest warta refaktoryzacji do własnej nazwanej klasy). Pouczającym przykładem jest użycie zwyczaju java.util.Comparator<T>do sortowania.

Oto przykład, jak możesz sortować pliki na String[]podstawie 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]"

Zwróć uwagę na zastosowaną tutaj sztuczkę porównania przez odejmowanie. Należy powiedzieć, że ta technika jest ogólnie zepsuta: ma zastosowanie tylko wtedy, gdy można zagwarantować, że nie będzie się przepełniać (tak jest w przypadku Stringdługości).

Zobacz też

smary wielogenowe
źródło
5
Większość innych zdarzeń można znaleźć jako EventListener(pod) implementacje w przeciętnej aplikacji Swing.
BalusC
@BalusC: dodano link do pytania „jak są używane”
polygenelubricants
@BalusC: stackoverflow niedawno dodał Linkedpasek boczny, więc staram się jak najlepiej go wykorzystać.
smary wielogenowe
12

Wraz z wprowadzeniem wyrażenia lambda w Javie 8 można teraz mieć metody anonimowe.

Załóżmy, że mam klasę Alphai chcę filtrować je Alphawedług określonego warunku. Aby to zrobić, możesz użyć pliku Predicate<Alpha>. To jest funkcjonalny interfejs, który ma metodę, testktóra akceptuje Alphai zwraca boolean.

Zakładając, że metoda filtru ma taki podpis:

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

W przypadku starego rozwiązania klasy anonimowej potrzebujesz czegoś takiego:

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

Z lambdami Java 8 możesz:

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

Aby uzyskać bardziej szczegółowe informacje, zobacz samouczek dotyczący wyrażeń lambda

Mark Rotteveel
źródło
2
Przydatne są również odniesienia do metod. np. sort (String :: compareToIgnoreCase) docs.oracle.com/javase/tutorial/java/javaOO/…
Josiah Yoder
9

Anonimowe klasy wewnętrzne implementujące lub rozszerzające interfejs istniejącego typu zostały wykonane w innych odpowiedziach, chociaż warto zauważyć, że można zaimplementować wiele metod (często na przykład ze zdarzeniami w stylu JavaBean).

Trochę rozpoznawalną cechą jest to, że chociaż anonimowe klasy wewnętrzne nie mają nazwy, mają typ. Do interfejsu można dodać nowe metody. Metody te można wywoływać tylko w ograniczonych przypadkach. Głównie bezpośrednio w samym newwyrażeniu iw klasie (w tym inicjatory instancji). Może to zmylić początkujących, ale rekurencja może być „interesująca”.

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);
}

(Pierwotnie napisałem to używając, nodea nie curw printmetodzie. Powiedz NIE przechwytywaniu „niejawnie final” miejscowych? )

Tom Hawtin - haczyk
źródło
nodenależy zadeklarować finaltutaj.
BalusC
@BalusC Niezły chwyt. Właściwie moim błędem było nie używać cur.
Tom Hawtin - tackline
@Tom: +1 fajna technika! Czy faktycznie jest używany w praktyce? Jakaś nazwa dla tego konkretnego wzoru?
smary wielogenowe
@polygenelubricants Nie o ile wiem. Kosztuje cały dodatkowy przedmiot! (I klasa). Podobnie było z idiomem z podwójnym nawiasem klamrowym. Prawidłowo myślącym ludziom nie przeszkadza idiom Execute Around.
Tom Hawtin - tackline
@polygenelubricants Właściwie nie wydaje mi się, aby istniało wiele (niezależnych) algorytmów rekurencyjnych. Szczególnie te, które nie są rekurencyjne (lub łatwo do tego celu) i nie można ich zaimplementować przez wywołanie metody public (zwróć uwagę na nieco nieistotne, "Node" +aby druga metoda była konieczna). / Nie mam imienia. Być może mógłbym stworzyć nazewnicze pytanie „ankietowe” (CW) i odrzucić je na zapomnienie.
Tom Hawtin - tackline
1

Tak, jeśli korzystasz z najnowszej wersji Java czyli wersji 8. Java8 umożliwia zdefiniowanie funkcji anonimowych, co było niemożliwe w poprzednich wersjach.

Weźmy przykład z dokumentacji java, aby dowiedzieć się, jak możemy deklarować anonimowe funkcje, klasy

Poniższy przykład, HelloWorldAnonymousClasses, używa klas anonimowych w instrukcjach inicjalizacji zmiennych lokalnych frenchGreeting i spanishGreeting, ale używa klasy lokalnej do inicjalizacji zmiennej 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();
    }            
}

Składnia klas anonimowych

Rozważmy instancję obiektu 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);
        }
    };

Anonimowe wyrażenie klasy składa się z następujących elementów:

  • newoperatora
  • Nazwa interfejsu do zaimplementowania lub klasy do rozszerzenia. W tym przykładzie klasa anonimowa implementuje interfejs HelloWorld.

  • Nawiasy zawierające argumenty konstruktora, tak jak w przypadku zwykłego wyrażenia tworzenia instancji klasy. Uwaga: kiedy implementujesz interfejs, nie ma konstruktora, więc używasz pustej pary nawiasów, jak w tym przykładzie.

  • Treść, która jest treścią deklaracji klasy. Mówiąc dokładniej, w treści deklaracje metod są dozwolone, ale instrukcje nie.

mumair
źródło