Как определить метод, который принимает лямбда в качестве параметра в Java 8?
В Java 8 методы могут быть созданы как лямбда-выражения и могут быть переданы по ссылке (с небольшой работой под капотом). Есть много примеров онлайн с лямбдами, которые создаются и используются с методами, но нет примеров того, как сделать метод, принимающий лямбду в качестве параметра. Какой синтаксис для этого?
MyClass.method((a, b) -> a+b);
class MyClass{
//How do I define this method?
static int method(Lambda l){
return l(5, 10);
}
}
17 ответов
Лямбда - это просто конструкция сайта вызова: получателю лямбды не нужно знать, что в нем участвует лямбда, вместо этого он принимает интерфейс с соответствующим методом.
Другими словами, вы определяете или используете функциональный интерфейс (то есть интерфейс с единственным методом), который принимает и возвращает именно то, что вы хотите.
Для этого Java 8 поставляется с набором часто используемых типов интерфейса в java.util.function
(спасибо Maurice Naftalin за подсказку о JavaDoc).
Для этого конкретного случая использования есть java.util.function.IntBinaryOperator
с одним int applyAsInt(int left, int right)
метод, чтобы вы могли написать свой method
как это:
static int method(IntBinaryOperator op){
return op.applyAsInt(5, 10);
}
Но вы также можете определить свой собственный интерфейс и использовать его так:
public interface TwoArgIntOperator {
public int op(int a, int b);
}
//elsewhere:
static int method(TwoArgIntOperator operator) {
return operator.op(5, 10);
}
Преимущество использования собственного интерфейса состоит в том, что вы можете иметь имена, которые более четко обозначают цель.
Чтобы использовать лямбда-выражение, вам нужно либо создать свой собственный функциональный интерфейс, либо использовать функциональный интерфейс Java для операций, которые требуют двух целых чисел и возвращают в качестве значения. IntBinaryOperator
Использование пользовательского функционального интерфейса
interface TwoArgInterface {
public int operation(int a, int b);
}
public class MyClass {
public static void main(String javalatte[]) {
// this is lambda expression
TwoArgInterface plusOperation = (a, b) -> a + b;
System.out.println("Sum of 10,34 : " + plusOperation.operation(10, 34));
}
}
Использование функционального интерфейса Java
import java.util.function.IntBinaryOperator;
public class MyClass1 {
static void main(String javalatte[]) {
// this is lambda expression
IntBinaryOperator plusOperation = (a, b) -> a + b;
System.out.println("Sum of 10,34 : " + plusOperation.applyAsInt(10, 34));
}
}
Другой пример, который я создал, здесь
Для функций, которые не имеют более 2 параметров, вы можете передавать их без определения собственного интерфейса. Например,
class Klass {
static List<String> foo(Integer a, String b) { ... }
}
class MyClass{
static List<String> method(BiFunction<Integer, String, List<String>> fn){
return fn.apply(5, "FooBar");
}
}
List<String> lStr = MyClass.method((a, b) -> Klass.foo((Integer) a, (String) b));
В BiFunction<Integer, String, List<String>>
, Integer
а также String
его параметры, и List<String>
это тип возвращаемого значения.
Для функции только с одним параметром вы можете использовать Function<T, R>
, где T
это его тип параметра, и R
это тип возвращаемого значения. Обратитесь к этой странице для всех интерфейсов, которые уже сделаны Java.
Для меня наиболее разумным решением является определение Callback
интерфейс:
interface Callback {
void call();
}
а затем использовать его в качестве параметра в функции, которую вы хотите вызвать:
void somewhereInYourCode() {
method(() -> {
// You've passed a lambda!
// method() is done, do whatever you want here.
});
}
void method(Callback callback) {
// Do what you have to do
// ...
// Don't forget to notify the caller once you're done
callback.call();
}
Просто точность, хотя
Лямбда - это не специальный интерфейс, класс или что-то еще, что вы можете объявить самостоятельно. Lambda
это просто имя, данное () -> {}
специальный синтаксис, который обеспечивает лучшую читаемость при передаче в качестве параметра интерфейсов с одним методом. Это было разработано, чтобы заменить это:
method(new Callback() {
@Override
public void call() {
// Classic interface implementation, lot of useless boilerplate code.
// method() is done, do whatever you want here.
}
});
Так что в приведенном выше примере, Callback
это не лямбда, это просто обычный интерфейс; lambda
имя синтаксиса ярлыка, который вы можете использовать для его реализации.
Существует общедоступная веб-версия JavaDocs Java 8 с поддержкой Lambda, ссылка на которую имеется по адресу http://lambdafaq.org/lambda-resources. (Очевидно, это должен быть комментарий к ответу Йоахима Сауэра, но я не могу войти в свою учетную запись SO с баллами репутации, которые мне нужны, чтобы добавить комментарий.) Сайт lambdafaq (я его поддерживаю) отвечает на этот и многие другие Java лямбда вопросов.
NB. Этот ответ был написан до того, как документация по Java 8 GA стала общедоступной. Я оставил его на месте, потому что Lambda FAQ может быть полезен для людей, которые знакомятся с функциями, представленными в Java 8.
Лямбда-выражение может быть передано в качестве аргумента. Чтобы передать лямбда-выражение в качестве аргумента, тип параметра (который получает лямбда-выражение в качестве аргумента) должен иметь тип функционального интерфейса.
Если есть функциональный интерфейс -
interface IMyFunc {
boolean test(int num);
}
И есть метод фильтра, который добавляет int в список, только если он больше 5. Обратите внимание, что метод фильтра имеет функциональный интерфейс IMyFunc в качестве одного из параметров. В этом случае лямбда-выражение может быть передано в качестве аргумента параметра метода.
public class LambdaDemo {
public static List<Integer> filter(IMyFunc testNum, List<Integer> listItems) {
List<Integer> result = new ArrayList<Integer>();
for(Integer item: listItems) {
if(testNum.test(item)) {
result.add(item);
}
}
return result;
}
public static void main(String[] args) {
List<Integer> myList = new ArrayList<Integer>();
myList.add(1);
myList.add(4);
myList.add(6);
myList.add(7);
// calling filter method with a lambda expression
// as one of the param
Collection<Integer> values = filter(n -> n > 5, myList);
System.out.println("Filtered values " + values);
}
}
Для тех, кто гуглит это, хорошим методом будет использование java.util.function.BiConsumer
, например:
Import java.util.function.Consumer
public Class Main {
public static void runLambda(BiConsumer<Integer, Integer> lambda) {
lambda.accept(102, 54)
}
public static void main(String[] args) {
runLambda((int1, int2) -> System.out.println(int1 + " + " + int2 + " = " + (int1 + int2)));
}
Оттиск будет: 166
Вы можете использовать функциональные интерфейсы, как указано выше. ниже приведены некоторые из примеров
Function<Integer, Integer> f1 = num->(num*2+1);
System.out.println(f1.apply(10));
Predicate<Integer> f2= num->(num > 10);
System.out.println(f2.test(10));
System.out.println(f2.test(11));
Supplier<Integer> f3= ()-> 100;
System.out.println(f3.get());
Надеюсь, это поможет
Лямбда - это не объект, а функциональный интерфейс. Можно определить столько функциональных интерфейсов, сколько они смогут, используя @FuntionalInterface в качестве аннотации
@FuntionalInterface
public interface SumLambdaExpression {
public int do(int a, int b);
}
public class MyClass {
public static void main(String [] args) {
SumLambdaExpression s = (a,b)->a+b;
lambdaArgFunction(s);
}
public static void lambdaArgFunction(SumLambdaExpression s) {
System.out.println("Output : "+s.do(2,5));
}
}
Выход будет следующим
Output : 7
Основная концепция лямбда-выражения заключается в определении собственной логики, но уже определенных аргументов. Таким образом, в приведенном выше коде вы можете изменить определение функции do в дополнение к любому другому определению, но ваши аргументы ограничены 2.
В основном, чтобы передать выражение lamda в качестве параметра, нам нужен тип, в котором мы можем его хранить. Так же, как целочисленное значение, мы храним в примитиве int или классе Integer. У Java нет отдельного типа для выражения лямда, вместо этого он использует интерфейс в качестве типа для хранения аргумента. Но этот интерфейс должен быть функциональным интерфейсом.
Ну, это легко. Целью лямбда-выражения является реализация функционального интерфейса. Это интерфейс только с одним методом. Вот удивительная статья о предопределенных и унаследованных функциональных интерфейсах.
В любом случае, если вы хотите реализовать свой собственный функциональный интерфейс, сделайте это. Просто для простого примера:
public interface MyFunctionalInterface {
String makeIt(String s);
}
Итак, давайте создадим класс, в котором мы создадим метод, который принимает тип MyFunctionalInterface:
public class Main {
static void printIt(String s, MyFunctionalInterface f) {
System.out.println(f.makeIt(s));
}
public static void main(String[] args) {
}
}
Последнее, что вы должны сделать, это передать реализацию MyFunctionalInterface методу, который мы определили:
public class Main {
static void printIt(String s, MyFunctionalInterface f) {
System.out.println(f.makeIt(s));
}
public static void main(String[] args) {
printIt("Java", s -> s + " is Awesome");
}
}
Это оно!
Сделайте следующее..
Вы объявили method(lambda l)
Все, что вы хотите сделать, это создать интерфейс с именем lambda
и объявить один абстрактный метод
public int add(int a,int b);
имя метода здесь не имеет значения..
Итак, когда ты звонишь MyClass.method( (a,b)->a+b)
Эта реализация (a,b)->a+b
будет добавлен в ваш интерфейс добавить метод. Так что всякий раз, когда вы вызываете l.add
он собирается взять эту реализацию и выполнить добавление a
а также b
а также return l.add(2,3)
вернусь 5
, - В основном это то, что делает лямбда..
Вот примерно то, как C# решает эту проблему (но выражается в виде кода Java). Что-то вроде этого может удовлетворить почти все ваши потребности:
import static org.util.function.Functions.*;
public class Test {
public static void main(String[] args)
{
Test.invoke((a, b) -> a + b);
}
public static void invoke(Func2<Integer, Integer, Integer> func)
{
System.out.println(func.apply(5, 6));
}
}
package org.util.function;
public interface Functions {
//Actions:
public interface Action {
public void apply();
}
public interface Action1<T1> {
public void apply(T1 arg1);
}
public interface Action2<T1, T2> {
public void apply(T1 arg1, T2 arg2);
}
public interface Action3<T1, T2, T3> {
public void apply(T1 arg1, T2 arg2, T3 arg3);
}
public interface Action4<T1, T2, T3, T4> {
public void apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
}
public interface Action5<T1, T2, T3, T4, T5> {
public void apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
}
public interface Action6<T1, T2, T3, T4, T5, T6> {
public void apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
}
public interface Action7<T1, T2, T3, T4, T5, T6, T7> {
public void apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
}
public interface Action8<T1, T2, T3, T4, T5, T6, T7, T8> {
public void apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
}
//Functions:
public interface Func<TResult> {
public TResult apply();
}
public interface Func1<T1, TResult> {
public TResult apply(T1 arg1);
}
public interface Func2<T1, T2, TResult> {
public TResult apply(T1 arg1, T2 arg2);
}
public interface Func3<T1, T2, T3, TResult> {
public TResult apply(T1 arg1, T2 arg2, T3 arg3);
}
public interface Func4<T1, T2, T3, T4, TResult> {
public TResult apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
}
public interface Func5<T1, T2, T3, T4, T5, TResult> {
public TResult apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
}
public interface Func6<T1, T2, T3, T4, T5, T6, TResult> {
public TResult apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
}
public interface Func7<T1, T2, T3, T4, T5, T6, T7, TResult> {
public TResult apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
}
public interface Func8<T1, T2, T3, T4, T5, T6, T7, T8, TResult> {
public TResult apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
}
}
ToDoubleFunction<DataSource> getValueOf(String methodName) {
return (DataSource ds) -> {
try {
Method method = DataSource.class.getMethod(methodName);
Integer i = (Integer) method.invoke(ds);
return Double.valueOf(i);
} catch (Exception throwables) {
throwables.printStackTrace();
}
return 0;
};
}
Если вы используете Java вместе с языком Kotlin (как мы это делаем в Android Development), вы можете передать лямбда-функцию без определения какого-либо дополнительного интерфейса:
import kotlin.jvm.functions.Function0;
import kotlin.jvm.functions.Function1;
import kotlin.jvm.functions.Function2;
void foo(Function0<Boolean> param1) //for zero param
{
param1.invoke();
}
void foo1(Function1<Integer, Boolean> param1) //for one param
{
param1.invoke(1);
}
void foo2(Function2<Integer, Boolean, Boolean> param1) //for two param
{
param1.invoke(1, true);
}
//how to call
void main()
{
foo(() -> {
return true;
});
foo1((var1) -> {
return true;
});
foo2((var1, var2) -> {
return true;
});
}
После того, как метод реализован интерфейсом, вы можете установить метод как params.
Существует гибкость в использовании лямбда в качестве параметра. Это позволяет функциональное программирование в Java. Основной синтаксис
param -> method_body
Ниже приведен способ определения метода, в котором в качестве параметра используется функциональный интерфейс (используется лямбда). а. если вы хотите определить метод, объявленный внутри функционального интерфейса, скажем, функциональный интерфейс задается в качестве аргумента / параметра для метода, вызываемого из main()
@FunctionalInterface
interface FInterface{
int callMeLambda(String temp);
}
class ConcreteClass{
void funcUsesAnonymousOrLambda(FInterface fi){
System.out.println("===Executing method arg instantiated with Lambda==="));
}
public static void main(){
// calls a method having FInterface as an argument.
funcUsesAnonymousOrLambda(new FInterface() {
int callMeLambda(String temp){ //define callMeLambda(){} here..
return 0;
}
}
}
/***********Can be replaced by Lambda below*********/
funcUsesAnonymousOrLambda( (x) -> {
return 0; //(1)
}
}
FInterface fi = (x) -> {return 0; };
funcUsesAnonymousOrLambda (р);
Здесь выше можно увидеть, как лямбда-выражение можно заменить интерфейсом.
Выше объясняется конкретное использование лямбда-выражения, есть и другие. ref Java 8 лямбда в лямбда не может изменить переменную из внешнего лямбда