Javy odpowiedniki Func i Action

87

Jakie są odpowiedniki Func i Action w Javie ?

Mam na myśli, zamiast pisać to sam:

public interface Func<TInput, TResult>
{
    TResult call(TInput target) throws Exception;
}
public interface Action<T>
{
    void call(T target) throws Exception;
}
zrywak234
źródło

Odpowiedzi:

97

W Javie 8 odpowiednikami są odpowiednio interfejsy java.util.function.Function<T, R>i java.util.function.Consumer<T>. Podobnie java.util.function.Predicate<T>jest równoważne z System.Predicate<T>. Jak wspomniano w innym miejscu, są to interfejsy zamiast delegatów.

Powiązane na bok: Obecnie intensywnie opieram się na następującej klasie narzędziowej, aby robić rzeczy związane z rozszerzeniem podobnym do LINQ:

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

W przeciwieństwie System.Linq.Enumerable.Where<TSource>i System.Linq.Enumerable.Select<TSource, TResult>LINQ-like metod przedstawiam tutaj nie są leniwi i przed powrotem zbiorów wynikowych do rozmówcy całkowicie przechodzić zbiorów źródłowych. Mimo to uważam je za przydatne do celów czysto syntaktycznych i w razie potrzeby można je rozlenić. Dany

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

Można wykonać następujące czynności:

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

Które wolę od następujących:

List<Widget> widgets = /* ... */;
List<Widget> filteredWidgets = new ArrayList<Widget>();
for (Widget w : widgets) {
  if (w.name().startsWith("some-prefix")) {
    filteredWidgets.add(w);
  }
}
Richard Cook
źródło
1
Naprawdę musimy zagłosować na tę odpowiedź, ponieważ to pytanie jest obecnie numerem 1 w wynikach wyszukiwania dla „odpowiednika akcji w Javie”, a teraz jest rok 2015, więc Java 8 jest znacznie lepsza niż to, co Java miała wcześniej i prawie naśladuje rzeczy .net punkt.
Robert C. Barth
1
Myślę, że miałeś na myśli Iterable <Widget> filterWidgets = IterableUtil.where (widżety, w -> w.name (). StartedWith ("jakiś-prefiks"));
electricbah
1
W dodatku Function<T, R>i Consumer<T>można znaleźć pełny zestaw wspólnych interfejsów funkcjonalnych że Java zapewnia tutaj .
Jämes
36

Interfejs wywoływalny jest podobny do Func.

Runnable interfejs jest podobny do Action.

Ogólnie rzecz biorąc, Java używa anonimowych klas wewnętrznych jako zamiennika dla delegatów C #. Na przykład tak dodajesz kod reagujący na naciśnięcie przycisku w GUI:

button.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) { 
          ...//code that reacts to the action... 
      }
});
Gregory Mostizky
źródło
13
Tym, co odróżnia Func od Callable, jest to, że istnieją ogólne przeciążenia dla maksymalnie 16 argumentów (Func <TResult>, Func <T, TResult>, Func <T1, T2, TResult> itd.). OTOH, Callable nie przyjmuje żadnych argumentów. Ponadto niemożliwe jest zaimplementowanie przeciążeń języka C # z powodu wymazywania typów w typach ogólnych.
Aleksandr Dubinsky
6

Elegancja z przeciążonych delegatów func (oprócz delegata vs anonimowej klasy emisji) jest to, że wspierają one od 0 do 16 argumentów ( Func<TResult>, Func<T, TResult>, Func<T1, T2, TResult>, itd.)

Niestety w Javie jest to niemożliwe z powodu wymazywania typów. Klasy nie mogą różnić się tylko parametrami typu ogólnego.

Java 8 teraz przynosi w zoo nazwach takich jak BiConsumerdla Action<T, T2>i, ponieważ Java nie pozwala prymitywne argumenty typu, BiIntConsumer. „Zoo” nie jest jednak zbyt duże i nie znam biblioteki, która by je powiększała. Była wspaniała propozycja dla literałów typu funkcji, (int, int) => voidale nie została przyjęta.

Aleksandr Dubinsky
źródło
3
Co ciekawe, na poziomie CLR klasy różniące się jedynie liczbą parametrów ogólnych mają różne nazwy. Func`1itd. To tylko C #, który odwzorowuje je na tę samą nazwę.
CodesInChaos
@CodesInChaos Ahh, bardzo interesujące. Szkoda, że ​​Java też tego nie zrobiła. Btw, Java 8 teraz przynosi w zoo nazwach takich jak BiConsumerdla Action<T, T2>i, ponieważ Java nie pozwala parametrów typu prymitywne BiIntConsumer. Pojawiła się propozycja literałów typu funkcji, (int, int) => voidale nie została ona przyjęta.
Aleksandr Dubinsky
5

Do Func<T>użytku: java.util.function.Supplier http://docs.oracle.com/javase/8/docs/api/java/util/function/Supplier.html

Tim Schruben
źródło
Supplierbyłoby równoważne Func<T>(w przeciwieństwie do Func<T1, T2>) nie Action. An Actionakceptuje żadnych argumentów i nie zwraca wyniku. (Inne wersje Actionakceptują różne liczby argumentów i nie zwracają żadnych wyników).
Servy
Tak, masz rację, mój błąd. Wpadłem na ten post, ponieważ szukałem Func<T>języka Java i omyłkowo zapamiętałem go jako Action<T>. Ups
Tim Schruben
I tak odpowiedź była dla mnie pomocna. Czy java ma też coś takiego Action<>: 0 wejść, 0 wyjść. Najlepiej z .andThen(...)funkcjonalnością.
Kolya Ivankov
Nie jestem świadomy niczego, co zapewnia framework Java, który jest podobny Action<>do 0 wejść i 0 wyjść. Ale pamiętaj, w Javie to tylko interfejsy. Możesz więc stworzyć własny do użytku.
Tim Schruben
Jest Runnabledla Action<>, chociaż nie jest tak ładny w użyciu, jak nowy funkcjonalny sprzęt Java 8.
Mark K Cowan
3

Naprawdę nie ma dla nich odpowiedników. Możesz tworzyć anonimowe klasy wewnętrzne w Javie, ale zazwyczaj istnieją specyficzne interfejsy, a nie takie ogólne, jak Func i Action.

AgileJon
źródło
3

Java nie ma koncepcji delegatów. Aby zapoznać się z rozwiązaniem obejścia, zobacz A Java Programmer Looks at C # Delegates :

Chociaż C # ma zestaw możliwości podobnych do Java, dodał kilka nowych i interesujących funkcji. Delegacja to możliwość traktowania metody jako obiektu pierwszej klasy. Delegat AC # jest używany, gdy programiści Java używaliby interfejsu z jedną metodą. W tym artykule omówiono użycie delegatów w języku C # i przedstawiono kod obiektu delegata języka Java, który może wykonywać podobną funkcję. Pobierz kod źródłowy tutaj.

Andrew Hare
źródło
3

Możesz użyć java.util.Function w ten sposób

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

Ale jeśli chcesz używać go z więcej niż jednym argumentem (tak jak robi to C # Func), musisz zdefiniować swoją wersję FunctionalInterface w następujący sposób

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

Następnie możesz użyć zmiennej liczba argumentów

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
źródło
1

W przypadku starszych wersji niż Java 8

Dla wywołań zwrotnych metod w C #, których użyłem w ten sposób:

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

i nazywam to:

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

Stworzyłem małe klasy abstrakcyjne w Javie

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

Metoda Java wygląda następująco:

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

Teraz dzwoniąc w Javie:

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

Działa to również poprzez przekazywanie / ustawianie wywołań zwrotnych do klas, w których chcesz je wywołać, ta sama metoda może być również użyta do tworzenia interfejsów:

package com.example.app.interfaces;
public interface MyInterface<T> {
    void makeDo(T obj);
    void makeAnotherDo();
}
Pierre
źródło