Эквиваленты функций Func и Action в Java

87

Какие эквиваленты Java для Func и Action ?

Я имею в виду, вместо того, чтобы писать это самостоятельно:

public interface Func<TInput, TResult>
{
    TResult call(TInput target) throws Exception;
}
public interface Action<T>
{
    void call(T target) throws Exception;
}
потрошитель234
источник
Также см stackoverflow.com/questions/7296606/...
Навфал

Ответы:

97

В Java 8 эквиваленты являются java.util.function.Function<T, R>и java.util.function.Consumer<T>интерфейсами соответственно. Аналогично java.util.function.Predicate<T>эквивалентно System.Predicate<T>. Как упоминалось в другом месте, это интерфейсы, а не делегаты.

Связано в стороне: в настоящее время я сильно полагаюсь на следующий служебный класс, чтобы делать вещи, подобные 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;
  }
}

В отличие System.Linq.Enumerable.Where<TSource>и System.Linq.Enumerable.Select<TSource, TResult>LINQ, как методы , которые я представляю здесь не ленятся и полностью пройти коллекции источников до возвращения коллекции результата вызывающего абонента. Тем не менее, я считаю их полезными для чисто синтаксических целей и при необходимости их можно сделать ленивыми. Дано

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

Можно сделать следующее:

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

Что я предпочитаю следующему:

List<Widget> widgets = /* ... */;
List<Widget> filteredWidgets = new ArrayList<Widget>();
for (Widget w : widgets) {
  if (w.name().startsWith("some-prefix")) {
    filteredWidgets.add(w);
  }
}
Ричард Кук
источник
1
Нам действительно нужно проголосовать за этот ответ, так как этот вопрос является текущим результатом поиска № 1 для «Java-эквивалента действия», а сейчас 2015 год, поэтому материал Java 8 намного лучше, чем был раньше, и почти имитирует материал .net в этом точка.
Роберт С. Барт
1
Я думаю, вы имели в виду Iterable <Widget> filterWidgets = IterableUtil.where (widgets, w -> w.name (). StartWith ("some-prefix"));
electricalbah
1
В добавлении Function<T, R>и Consumer<T>вы можете найти полный набор общих функциональных интерфейсов, Java предоставляет здесь .
Jämes
36

Вызываемый интерфейс похож на Func.

Runnable интерфейс похож на Action.

В общем, Java использует анонимные внутренние классы в качестве замены делегатов C #. Например, вот как вы добавляете код для реакции на нажатие кнопки в графическом интерфейсе:

button.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) { 
          ...//code that reacts to the action... 
      }
});
Григорий Мостицкий
источник
13
Что отличает Func от Callable, так это то, что существуют общие перегрузки для до 16 аргументов (Func <TResult>, Func <T, TResult>, Func <T1, T2, TResult> и т. Д.). OTOH, Callable не принимает аргументов. Более того, невозможно реализовать перегрузки C # из-за стирания типов в дженериках.
Александр Дубинский
6

Элегантность перегруженных делегатов Func (помимо делегата против анонимного вопроса класса) является то , что они поддерживают от 0 до 16 аргументов ( Func<TResult>, Func<T, TResult>, Func<T1, T2, TResult>и т.д.)

К сожалению, в Java это невозможно из-за стирания типа. Классы не могут различаться только параметрами универсального типа.

Java-теперь приносит в зоопарке имен , как BiConsumerдля Action<T, T2>и, поскольку Java не позволяет аргументы примитивного типа, BiIntConsumer. Однако «зоопарк» не очень большой, и я не знаю библиотеки, которая бы его расширяла. Было прекрасное предложение для литералов функционального типа, (int, int) => voidно оно не было принято.

Александр Дубинский
источник
3
Интересно, что на уровне CLR классы, которые отличаются только количеством общих параметров, имеют разные имена. Func`1и т.д. Просто C # отображает их на одно и то же имя.
CodesInChaos
@CodesInChaos Ах, очень интересно. Жаль, что Java тоже этого не сделала. Кстати, Java-теперь приносит в зоопарке имен , как BiConsumerдля Action<T, T2>и, поскольку Java не позволяет параметры примитивного типа, BiIntConsumer. Было предложение о литералах типа функции, (int, int) => voidно оно не было принято.
Александр Дубинский
5

Для Func<T>использования: java.util.function.Supplier http://docs.oracle.com/javase/8/docs/api/java/util/function/Supplier.html

Тим Шрубен
источник
Supplierбудет эквивалентно Func<T>(в отличие от Func<T1, T2>) not Action. An не Actionпринимает аргументов и не возвращает результата. (Другие версии Actionпринимают различное количество аргументов и не возвращают результата.)
Servy
Да вы правы, моя ошибка. Я наткнулся на этот пост, потому что искал Func<T>Java-файл и по ошибке запомнил его как Action<T>. Ой
Тим Шрубен
В любом случае ответ был для меня полезен. Есть ли в java что-то вроде Action<>: 0 входов, 0 выходов. В лучшем случае с .andThen(...)функциональностью.
Коля Иваньков
Я не знаю ничего, что предоставляет среда Java, что похоже Action<>на 0 входов и 0 выходов. Но помните, в Java это всего лишь интерфейсы. Итак, вы можете создать свой собственный для использования.
Тим Шрубен
Есть Runnablefor Action<>, хотя его не так удобно использовать, как новые функциональные возможности Java 8.
Mark K Cowan
3

Им действительно нет эквивалентов. Вы можете создавать анонимные внутренние классы в Java, но обычно это специфические интерфейсы, а не такие общие, как Func и Action.

AgileJon
источник
3

В Java нет концепции делегатов. Обходной подход см. В статье " Программист на Java смотрит на делегатов C #". :

Хотя C # имеет набор возможностей, аналогичный Java, он добавил несколько новых и интересных функций. Делегирование - это возможность рассматривать метод как первоклассный объект. Делегат AC # используется там, где разработчики Java будут использовать интерфейс с одним методом. В этой статье обсуждается использование делегатов в C # и представлен код для объекта Java Delegate, который может выполнять аналогичную функцию. Загрузите исходный код здесь.

Эндрю Хэйр
источник
3

Вы можете использовать java.util.Function, как это

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

Но если вы должны использовать его с более чем одним аргументом (как это делает C # Func), тогда вы должны определить свою версию FunctionalInterface следующим образом

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

Затем вы можете использовать с переменной без аргументов

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
источник
1

Для более старых версий, чем Java 8

Для обратных вызовов методов в C #, которые я использовал так:

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

и называя это:

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

Я сделал небольшие абстрактные классы на 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

Метод Java выглядит так:

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

Теперь при вызове на 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
    }
});

Это также работает, передавая / устанавливая ваши обратные вызовы классам, в которых вы хотите их вызывать, тот же метод можно использовать и для создания интерфейсов:

package com.example.app.interfaces;
public interface MyInterface<T> {
    void makeDo(T obj);
    void makeAnotherDo();
}
Пьер
источник