Equivalentes de Java de Func y Action

86

¿Cuáles son los equivalentes de Func y Action en Java ?

Quiero decir, en lugar de escribir esto por mi cuenta:

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

Respuestas:

96

En Java 8, los equivalentes son las interfaces java.util.function.Function<T, R>y java.util.function.Consumer<T>respectivamente. De manera similar, java.util.function.Predicate<T>es equivalente a System.Predicate<T>. Como se mencionó en otra parte, estas son interfaces en lugar de delegados.

Aparte de lo relacionado: actualmente me estoy apoyando en gran medida en la siguiente clase de utilidad para hacer cosas de métodos de extensión similares a 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;
  }
}

A diferencia System.Linq.Enumerable.Where<TSource>y System.Linq.Enumerable.Select<TSource, TResult>el LINQ como métodos que presento aquí no son perezosos y atravesar completamente de las colecciones de origen antes de regresar las colecciones de resultados al llamador. Aún así, los encuentro útiles para propósitos puramente sintácticos y podrían volverse perezosos si fuera necesario. Dado

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

Uno puede hacer lo siguiente:

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

Que prefiero a lo siguiente:

List<Widget> widgets = /* ... */;
List<Widget> filteredWidgets = new ArrayList<Widget>();
for (Widget w : widgets) {
  if (w.name().startsWith("some-prefix")) {
    filteredWidgets.add(w);
  }
}
Richard Cook
fuente
1
Realmente necesitamos votar a favor de esta respuesta ya que esta pregunta es el resultado de búsqueda número 1 actual para "Java equivalente de acción" y ahora es 2015, por lo que las cosas de Java 8 son mucho mejores que las que tenía Java antes y casi imitan las cosas de .net en este punto.
Robert C. Barth
1
Creo que te refieres a Iterable <Widget> filterWidgets = IterableUtil.where (widgets, w -> w.name (). StartsWith ("some-prefix"));
electricbah
1
Además de Function<T, R>y Consumer<T>, puede encontrar el conjunto completo de interfaces funcionales comunes que proporciona Java aquí .
Jämes
36

La interfaz invocable es similar a Func.

Runnable interfaz es similar a Action.

En general, Java usa clases internas anónimas como reemplazo de los delegados de C #. Por ejemplo, así es como agrega código para reaccionar al presionar un botón en la GUI:

button.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) { 
          ...//code that reacts to the action... 
      }
});
Gregory Mostizky
fuente
13
Lo que distingue a Func de Callable es que existen sobrecargas genéricas para hasta 16 argumentos (Func <TResult>, Func <T, TResult>, Func <T1, T2, TResult>, etc.). OTOH, Callable no acepta argumentos. Además, es imposible implementar las sobrecargas de C # debido al borrado de tipo en los genéricos.
Aleksandr Dubinsky
6

La elegancia de los delegados Func sobrecargados (además del delegado vs cuestión de clase anónima) es que se apoyan de 0 a 16 argumentos ( Func<TResult>, Func<T, TResult>,Func<T1, T2, TResult> , etc.)

Desafortunadamente, esto es imposible en Java debido al borrado de tipos. Las clases no pueden diferir únicamente por parámetros de tipo genérico.

Java 8 ahora trae un zoológico de nombres como BiConsumerparaAction<T, T2> y, a causa de Java no permite argumentos de tipo primitivo, BiIntConsumer. El "zoológico", sin embargo, no es muy grande y no conozco una biblioteca que lo amplíe. Hubo una propuesta maravillosa para literales de tipo de función como, (int, int) => voidpero no fue adoptada.

Aleksandr Dubinsky
fuente
3
Curiosamente, en el nivel CLR, las clases que se diferencian solo por el número de parámetros genéricos tienen nombres diferentes. Func`1etc. Es solo C # el que los asigna al mismo nombre.
CodesInChaos
@CodesInChaos Ahh, muy interesante. Lástima que Java no lo haya hecho de esta manera también. Por cierto, Java 8 ahora trae en un zoológico de nombres como BiConsumerpara Action<T, T2>y, a causa de Java no permite parámetros de tipo primitivo, BiIntConsumer. Hubo una propuesta para literales de tipo de función como, (int, int) => voidpero no fue adoptada.
Aleksandr Dubinsky
5

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

Tim Schruben
fuente
Suppliersería equivalente a Func<T>(en contraposición a Func<T1, T2>) no Action. An Actionno acepta argumentos y no devuelve ningún resultado. (Otras versiones de Actionaceptan varios números de argumentos y no devuelven ningún resultado.)
Servy
Sí, tienes razón, mi error. Me encontré con esta publicación porque estaba buscando Func<T>para Java y lo recordé por error como Action<T>. Vaya
Tim Schruben
La respuesta fue útil para mí de todos modos. ¿Java también tiene algo como Action<>: 0 entradas, 0 salidas? En el mejor de los casos con .andThen(...)funcionalidad.
Kolya Ivankov
No tengo conocimiento de nada proporcionado por el marco de Java que sea como Action<>con 0 entradas y 0 salidas. Pero recuerde, en Java estas son solo interfaces. Por lo tanto, puede crear el suyo propio para usar.
Tim Schruben
Hay Runnablepara Action<>, aunque no es tan bonito de usar como el nuevo material funcional de Java 8.
Mark K Cowan
3

Realmente no hay equivalentes para esos. Puede crear clases internas anónimas en Java, pero tiende a haber interfaces específicas en lugar de genéricas como Func y Action.

AgileJon
fuente
3

Java no tiene el concepto de delegados. Para obtener una solución alternativa, consulte Un programador de Java observa a los delegados de C # :

Si bien C # tiene un conjunto de capacidades similares a Java, ha agregado varias características nuevas e interesantes. La delegación es la capacidad de tratar un método como un objeto de primera clase. El delegado AC # se usa cuando los desarrolladores de Java usarían una interfaz con un solo método. En este artículo, se analiza el uso de delegados en C # y se presenta el código para un objeto Delegado de Java que puede realizar una función similar. Descarga el código fuente aquí.

Andrew Hare
fuente
3

Puede usar java.util.Function así

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

Pero si va a usarlo con más de un argumento (como lo hace C # Func), entonces debe definir su versión de FunctionalInterface de la siguiente manera

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

Entonces puedes usar con variable no de argumentos

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();
usuario2739602
fuente
1

Para versiones anteriores a Java 8

Para devoluciones de llamada de método en C # que utilicé así:

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

y llamarlo:

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

He realizado pequeñas clases abstractas en Java.

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

El método Java se ve así:

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

Ahora, al llamarlo en Java:

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

Esto también funciona al pasar / configurar sus devoluciones de llamada a las clases en las que desea llamarlas, el mismo método también se puede usar para crear interfaces:

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