Javas Äquivalente von Func und Action

84

Was sind Javas Äquivalente von Func und Action ?

Ich meine, anstatt dies selbst zu schreiben:

public interface Func<TInput, TResult>
{
    TResult call(TInput target) throws Exception;
}
public interface Action<T>
{
    void call(T target) throws Exception;
}
ripper234
quelle

Antworten:

95

In Java 8 sind die Äquivalente der java.util.function.Function<T, R>und java.util.function.Consumer<T>Schnittstellen sind. Ebenso java.util.function.Predicate<T>ist äquivalent zu System.Predicate<T>. Wie an anderer Stelle erwähnt, handelt es sich hierbei um Schnittstellen anstelle von Delegaten.

Nebenbei bemerkt: Ich stütze mich derzeit stark auf die folgende Utility-Klasse, um LINQ-ähnliche Erweiterungsmethoden zu erstellen:

abstract class IterableUtil {
  public static <T> Iterable<T> where(Iterable<T> items, Predicate<T> predicate) {
    ArrayList<T> result = new ArrayList<T>();
    for (T item : items) {
      if (predicate.test(item)) {
        result.add(item);
      }
    }
    return result;
  }

  public static <T, R> Iterable<R> select(Iterable<T> items, Function<T, R> func) {
    ArrayList<R> result = new ArrayList<R>();
    for (T item : items) {
      result.add(func.apply(item));
    }
    return result;
  }
}

Im Gegensatz zu System.Linq.Enumerable.Where<TSource>und System.Linq.Enumerable.Select<TSource, TResult>den LINQ-ähnlichen Methoden, die ich hier vorstelle, sind sie nicht faul und durchlaufen die Quellensammlungen vollständig, bevor die Ergebnissammlungen an den Aufrufer zurückgegeben werden. Trotzdem finde ich sie für rein syntaktische Zwecke nützlich und könnte bei Bedarf faul gemacht werden. Gegeben

class Widget {
  public String name() { /* ... */ }
}

Man kann folgendes tun:

List<Widget> widgets = /* ... */;
Iterable<Widget> filteredWidgets = IterableUtil.where(widgets, w -> w.name().startsWith("some-prefix"));

Was ich dem folgenden vorziehe:

List<Widget> widgets = /* ... */;
List<Widget> filteredWidgets = new ArrayList<Widget>();
for (Widget w : widgets) {
  if (w.name().startsWith("some-prefix")) {
    filteredWidgets.add(w);
  }
}
Richard Cook
quelle
1
Wir müssen diese Antwort wirklich abstimmen, da diese Frage das aktuelle Suchergebnis Nr. 1 für "Java-Äquivalent zur Aktion" ist und es jetzt 2015 ist. Das Java 8-Zeug ist also viel besser als das, was Java zuvor hatte, und ahmt fast das .net-Zeug nach Punkt.
Robert C. Barth
1
Ich denke, Sie meinten Iterable <Widget> filteredWidgets = IterableUtil.where (Widgets, w -> w.name (). StartsWith ("some-prefix"));
Electricbah
1
Zusätzlich von Function<T, R>und Consumer<T>können Sie den vollen Satz von gemeinsamen funktionalen Schnittstellen finden , dass Java bietet hier .
ZenLulz
34

Abrufbar Schnittstelle ähnelt Func.

Runnable Schnittstelle ähnelt der Aktion.

Im Allgemeinen verwendet Java anonyme innere Klassen als Ersatz für C # -Delegierte. So fügen Sie beispielsweise Code hinzu, um auf das Drücken von Tasten in der GUI zu reagieren:

button.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) { 
          ...//code that reacts to the action... 
      }
});
Gregory Mostizky
quelle
13
Was Func von Callable unterscheidet, ist, dass es generische Überladungen für bis zu 16 Argumente gibt (Func <Tresult>, Func <T, TResult>, Func <T1, T2, TResult> usw.). OTOH, Callable akzeptiert keine Argumente. Darüber hinaus ist es aufgrund der Typlöschung in Generika unmöglich, C # -Überladungen zu implementieren.
Aleksandr Dubinsky
6

Die Eleganz der überladenen Func Delegierten (neben den Delegierten gegen anonyme Klasse Ausgabe) ist , dass sie 0-16 Argumente unterstützen ( Func<TResult>, Func<T, TResult>, Func<T1, T2, TResult>, etc.)

Leider ist dies in Java aufgrund der Typlöschung nicht möglich. Klassen können sich nicht allein durch generische Typparameter unterscheiden.

Java 8 bringt jetzt einen Zoo mit Namen wie BiConsumerfür Action<T, T2>und, da Java keine primitiven Typargumente zulässt BiIntConsumer. Der "Zoo" ist jedoch nicht sehr groß, und mir ist keine Bibliothek bekannt, die ihn erweitert. Es gab einen wunderbaren Vorschlag für Literale vom Funktionstyp wie, (int, int) => voidaber er wurde nicht angenommen.

Aleksandr Dubinsky
quelle
3
Interessanterweise haben Klassen auf CLR-Ebene, die sich nur durch die Anzahl der generischen Parameter unterscheiden, unterschiedliche Namen. Func`1usw. Es ist nur C #, das diese dem gleichen Namen zuordnet.
CodesInChaos
@CodesInChaos Ahh, sehr interessant. Schade, dass Java das auch nicht so gemacht hat. Übrigens bringt Java 8 jetzt einen Zoo mit Namen wie BiConsumerfür Action<T, T2>und, da Java keine primitiven Typparameter zulässt BiIntConsumer. Es gab einen Vorschlag für Funktionstyp-Literale wie, der (int, int) => voidjedoch nicht angenommen wurde.
Aleksandr Dubinsky
5

Zur Func<T>Verwendung: java.util.function.Supplier http://docs.oracle.com/javase/8/docs/api/java/util/function/Supplier.html

Tim Schruben
quelle
Supplierwäre gleichbedeutend mit Func<T>(im Gegensatz zu Func<T1, T2>) nicht Action. An Actionakzeptiert keine Argumente und gibt kein Ergebnis zurück. (Andere Versionen von Actionakzeptieren verschiedene Anzahlen von Argumenten und geben kein Ergebnis zurück.)
Servy
Ja, du hast recht, mein Fehler. Ich bin auf diesen Beitrag gestoßen, weil ich nach Func<T>Java gesucht habe und mich fälschlicherweise daran erinnert habe Action<T>. Oops
Tim Schruben
Die Antwort war trotzdem hilfreich für mich. Hat Java auch so etwas wie Action<>: 0 Eingänge, 0 Ausgänge. Bestenfalls mit .andThen(...)Funktionalität.
Kolya Ivankov
Mir ist nichts bekannt, was vom Java-Framework bereitgestellt wird Action<>und mit 0 Eingaben und 0 Ausgaben vergleichbar ist. Aber denken Sie daran, in Java sind dies nur Schnittstellen. So können Sie Ihre eigenen erstellen, um sie zu verwenden.
Tim Schruben
Es gibt Runnablefür Action<>, obwohl es nicht ganz so hübsch zu bedienen ist wie das neue Java 8-Funktionsmaterial.
Mark K Cowan
3

Es gibt wirklich keine Entsprechungen für diese. Sie können in Java anonyme innere Klassen erstellen, es gibt jedoch eher bestimmte Schnittstellen als generische wie Func und Action.

AgileJon
quelle
3

Java hat nicht das Konzept von Delegierten. Eine Problemumgehung finden Sie unter Ein Java-Programmierer betrachtet C # -Delegierte :

Während C # über eine Reihe von Funktionen verfügt, die Java ähneln, wurden einige neue und interessante Funktionen hinzugefügt. Delegation ist die Fähigkeit, eine Methode als erstklassiges Objekt zu behandeln. AC # -Delegat wird verwendet, wenn Java-Entwickler eine Schnittstelle mit einer einzigen Methode verwenden würden. In diesem Artikel wird die Verwendung von Delegaten in C # erläutert und Code für ein Java-Delegatenobjekt vorgestellt, das eine ähnliche Funktion ausführen kann. Laden Sie hier den Quellcode herunter.

Andrew Hare
quelle
3

Sie können java.util.Function wie folgt verwenden

Function<Employee, String> f0 = (e) -> e.toString();

Wenn Sie es jedoch mit mehr als einem Argument verwenden möchten (wie es C # Func tut), müssen Sie Ihre Version von FunctionalInterface wie folgt definieren

@FunctionalInterface
public interface Func2Args<T, T1, R> {
    R apply(T t, T1 t1);
}

@FunctionalInterface
public interface Func3Args<T,T1,T2,R> {
    R apply(T t, T1 t1, T2 t2);
}

Dann können Sie mit der Variablen Anzahl der Argumente verwenden

Func2Args<Employee,Employee,String> f2 = (e, e2) -> e.toString() + 
e2.toString();

Func3Args<Employee,Employee,Employee,String> f3 = (e, e2, e3) -> 
e.toString() + e2.toString() + e3.toString();
user2739602
quelle
1

Für ältere Versionen als Java 8

Für Methodenrückrufe in C #, die ich wie folgt verwendet habe:

public void MyMethod(string par1, string par2, Action<int> callback, Action<int, string> callback2)
{
    //Async Code 
        callback.invoke(1);
        callback2.invoke(4, "str");
}

und nenne es:

utils.MyMethod("par1", "par2", (i) =>
{
    //cb result
}, (i, str) =>
{
    //cb2 result
});

Ich habe kleine abstrakte Klassen in Java gemacht

package com.example.app.callbacks;
public abstract class Callback1<T> {
    public void invoke(T obj) {}
}

package com.example.app.callbacks;
public abstract class Callback2<T, T2> {
    public void invoke(T obj, T2 obj2) {}
}

package com.example.app.callbacks;
public abstract class Callback3<T, T2, T3> {
    public void invoke(T obj, T2 obj2, T3 obj3) {}
}

...ETC

Die Java-Methode sieht folgendermaßen aus:

public void myMethod(String par1, String par2, final Callback1<int> callback, final Callback2<int, String> callback2) {
    //Async Code 
        callback.invoke(1);
        callback2.invoke(4, "str");
}

Wenn Sie es jetzt in Java aufrufen:

utils.myMethod("par1", "par2", new Callback<int>() {
    @Override
    public void invoke(int obj) {
        super.invoke(obj);
        //cb result
    }
}, new Callback2<int, String>() {
    @Override
    public void invoke(int obj, String obj2) {
        super.invoke(obj, obj2);
        //cb2 result
    }
});

Dies funktioniert auch, indem Sie Ihre Rückrufe an die Klassen übergeben / festlegen, in denen Sie sie aufrufen möchten. Dieselbe Methode kann auch zum Erstellen von Schnittstellen verwendet werden:

package com.example.app.interfaces;
public interface MyInterface<T> {
    void makeDo(T obj);
    void makeAnotherDo();
}
Pierre
quelle