Wskaźniki funkcji w Javie

To może być coś pospolitego i trywialnego, ale wydaje mi się, że mam problem ze znalezieniem konkretnej odpowiedzi. W C# istnieje pojęcie delegatów, które silnie wiąże się z ideą wskaźników funkcyjnych z C++. Czy istnieje podobna funkcjonalność w Javie? Biorąc pod uwagę, że wskaźniki są nieco nieobecne, jaki jest w tym najlepszy sposób? I żeby było jasne, mówimy tu o pierwszej klasie.

Author: Ben Lakey, 2009-07-02

11 answers

Idiom Java dla funkcji przypominających wskaźnik jest anonimową klasą implementującą interfejs, np.

Collections.sort(list, new Comparator<MyClass>(){
    public int compare(MyClass a, MyClass b)
    {
        // compare objects
    }
});

Update: powyższe jest konieczne w wersjach Java poprzedzających Java 8. Teraz mamy znacznie ładniejsze alternatywy, a mianowicie lambda:

list.sort((a, b) -> a.isGreaterThan(b));

I odniesienia do metody:

list.sort(MyClass::isGreaterThan);
 117
Author: Michael Borgwardt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-23 09:48:14

Możesz zastąpić wskaźnik funkcji interfejsem. Powiedzmy, że chcesz uruchomić kolekcję i zrobić coś z każdym elementem.

public interface IFunction {
  public void execute(Object o);
}

To jest interfejs, który możemy przekazać do jakiegoś powiedzmy CollectionUtils2.doFunc (zbiór c, Ifunc f).

public static void doFunc(Collection c, IFunction f) {
   for (Object o : c) {
      f.execute(o);
   }
}

Jako przykład powiedzmy, że mamy zbiór liczb i chcemy dodać 1 do każdego elementu.

CollectionUtils2.doFunc(List numbers, new IFunction() {
    public void execute(Object o) {
       Integer anInt = (Integer) o;
       anInt++;
    }
});
 62
Author: raupach,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-02 09:38:12

Możesz użyć do tego refleksji.

Przekazuje jako parametr obiekt i nazwę metody (jako łańcuch znaków), a następnie wywołuje metodę. Na przykład:

Object methodCaller(Object theObject, String methodName) {
   return theObject.getClass().getMethod(methodName).invoke(theObject);
   // Catch the exceptions
}

A następnie użyj go jak w:

String theDescription = methodCaller(object1, "toString");
Class theClass = methodCaller(object2, "getClass");

Oczywiście, sprawdź wszystkie wyjątki i dodaj potrzebne odlewy.

 41
Author: zoquete,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-12 13:56:36

Nie, FUNKCJE NIE są obiektami pierwszej klasy w Javie. Możesz zrobić to samo, implementując klasę handler - w ten sposób wywołania zwrotne są implementowane w swingu itp.

Istnieją jednak propozycje zamknięcia (oficjalna nazwa tego, o czym mówisz) w przyszłych wersjach Javy - Javaworld ma ciekawy artykuł.

 20
Author: jwoolard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-02 10:47:22

To przywodzi na myśl egzekucję Steve 'a Yegge' A w Królestwie rzeczowników. Zasadniczo stwierdza, że Java potrzebuje obiektu dla każdej akcji, a zatem nie ma encji "tylko czasowników", takich jak Wskaźniki funkcji.

 13
Author: Yuval F,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-14 13:40:12

Aby osiągnąć podobną funkcjonalność, możesz użyć anonimowych klas wewnętrznych.

If you were to define a interface Foo:

interface Foo {
    Object myFunc(Object arg);
}

Utwórz metodę bar, która otrzyma 'wskaźnik funkcji' jako argument:

public void bar(Foo foo) {
    // .....
    Object object = foo.myFunc(argValue);
    // .....
}

Na koniec wywołaj metodę w następujący sposób:

bar(new Foo() {
    public Object myFunc(Object arg) {
        // Function code.
    }
}
 6
Author: Kingamajick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-02 11:03:21

Nie ma czegoś takiego w Javie. Musisz zawinąć swoją funkcję w jakiś obiekt i przekazać odniesienie do tego obiektu, aby przekazać odniesienie do metody na tym obiekcie.

Składniowo, może to być złagodzone do pewnego stopnia przez użycie anonimowych klas zdefiniowanych w miejscu lub anonimowych klas zdefiniowanych jako zmienne członkowskie klasy.

Przykład:

class MyComponent extends JPanel {
    private JButton button;
    public MyComponent() {
        button = new JButton("click me");
        button.addActionListener(buttonAction);
        add(button);
    }

    private ActionListener buttonAction = new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            // handle the event...
            // note how the handler instance can access 
            // members of the surrounding class
            button.setText("you clicked me");
        }
    }
}
 5
Author: VoidPointer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-04 19:25:14

Java8 wprowadził odwołania do metod lambda i . Więc jeśli twoja funkcja pasuje do functional interface (możesz utworzyć swój własny), możesz użyć referencji metody w tym przypadku.

Java dostarcza zestaw wspólnych funkcjonalnych interfejsów . można natomiast wykonać następujące czynności:

public class Test {
   public void test1(Integer i) {}
   public void test2(Integer i) {}
   public void consumer(Consumer<Integer> a) {
     a.accept(10);
   }
   public void provideConsumer() {
     consumer(this::test1);   // method reference
     consumer(x -> test2(x)); // lambda
   }
}
 5
Author: Alex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-20 11:23:29

Zaimplementowałem obsługę callback/delegate w Javie przy użyciu reflection. Szczegóły i robocze źródło są dostępne na mojej stronie .

Jak To Działa

Mamy klasę zasad o nazwie Callback z zagnieżdżoną klasą o nazwie WithParms. API, które potrzebuje callback, weźmie obiekt Callback jako parametr i, jeśli jest to konieczne, utworzy Callback.WithParms jako zmienna metody. Ponieważ wiele zastosowań tego obiektu będzie rekurencyjnych, działa to bardzo czysto.

Ponieważ wydajność nadal jest dla mnie priorytetem, nie chciałem być zobowiązany do tworzenia tablicy obiektów, która przechowuje parametry każdego wywołania - w końcu w dużej strukturze danych może być tysiące elementów, a w scenariuszu przetwarzania wiadomości możemy skończyć przetwarzaniem tysięcy struktur danych na sekundę.

Aby była bezpieczna dla wątków, tablica parametrów musi istnieć unikalnie dla każdego wywołania metody API, a dla efektywności ten sam powinien być używany przy każdym wywołaniu wywołania zwrotnego; potrzebowałem drugiego obiektu, który byłby Tani do wytworzenia, aby powiązać wywołanie zwrotne z tablicą parametrów dla wywołania. Ale w niektórych scenariuszach, Wywoływacz miałby już tablicę parametrów z innych powodów. Z tych dwóch powodów tablica parametrów nie należy do obiektu Callback. Również wybór wywołania (przekazywanie parametrów jako tablica lub jako poszczególne obiekty) należy do rąk API za pomocą wywołanie zwrotne umożliwiające użycie dowolnego wywołania najlepiej nadaje się do jego wewnętrznego działania.

Zagnieżdżona Klasa WithParms jest wtedy opcjonalna i służy dwóm celom, zawiera tablicę obiektu parametru potrzebną do wywołania wywołania zwrotnego oraz dostarcza 10 przeciążonych metod invoke () (z 1 do 10 parametrami), które ładują tablicę parametrów i wywołują obiekt wywołania zwrotnego.

 3
Author: Lawrence Dol,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-16 00:42:04

Sprawdź jak zostały one zaimplementowane w bibliotece lambdaj. W rzeczywistości mają zachowanie bardzo podobne do C# delegatów:

Http://code.google.com/p/lambdaj/wiki/Closures

 2
Author: Mario Fusco,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-12 13:23:44

W stosunku do większości ludzi Jestem tu nowy w Javie, ale ponieważ nie widziałem podobnej sugestii, mam inną alternatywę do zaproponowania. Nie jestem pewien, czy to dobra praktyka, czy nie, lub nawet zasugerował przed i po prostu nie dostał. Po prostu podoba mi się, ponieważ myślę, że jest samoopisowy.

 /*Just to merge functions in a common name*/
 public class CustomFunction{ 
 public CustomFunction(){}
 }

 /*Actual functions*/
 public class Function1 extends CustomFunction{
 public Function1(){}
 public void execute(){...something here...}
 }

 public class Function2 extends CustomFunction{
 public Function2(){}
 public void execute(){...something here...}
 }

 .....
 /*in Main class*/
 CustomFunction functionpointer = null;

Następnie w zależności od aplikacji Przypisz

 functionpointer = new Function1();
 functionpointer = new Function2();

Itd.

I wywołanie przez

 functionpointer.execute();
 0
Author: Özgen Eren,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-10 11:38:56