Java 8에서 람다를 매개 변수로 사용하는 메소드를 어떻게 정의합니까?


363

Java 8에서 메소드는 Lambda 표현식으로 작성 될 수 있으며 참조로 전달 될 수 있습니다 (후드에서 약간의 작업만으로). 람다는 작성되고 메소드와 함께 사용되는 많은 예제가 온라인에서 있지만 람다를 매개 변수로 사용하는 메소드를 작성하는 방법에 대한 예제는 없습니다. 그 구문은 무엇입니까?

MyClass.method((a, b) -> a+b);


class MyClass{
  //How do I define this method?
  static int method(Lambda l){
    return l(5, 10);
  }
}

29
좋은 질문. 그리고 당신은 옳습니다 : 어떤 튜토리얼도 그 부분을 포함하지 않습니다.
Martin

답변:


247

람다는 순전히 호출 사이트 구조입니다. 람다의 수신자는 람다가 관련된 것을 알 필요가 없으며 대신 적절한 방법으로 인터페이스를 허용합니다.

다시 말해, 원하는 것을 정확하게 수용하고 반환하는 기능적 인터페이스 (예 : 단일 메소드가있는 인터페이스)를 정의하거나 사용합니다.

이 Java 8에는 일반적으로 사용되는 인터페이스 유형 세트가 포함되어 있습니다 java.util.function( JavaDoc에 대한 힌트는 Maurice Naftalin 덕분입니다 ).

이 특정 사용 사례를 들어 거기 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);
}

자체 인터페이스를 사용하면 의도를보다 명확하게 나타내는 이름을 가질 수 있다는 이점이 있습니다.


5
사용할 내장 인터페이스가 있습니까, 아니면 원하는 모든 람다에 대한 인터페이스를 만들어야합니까?
Marius

재사용 성 대 서술 적 이름 딜레마에 대한 좋은 타협은 지정된 방법을 재정의하지 않고 내장 인터페이스를 확장하는 것입니다. 이는 단 한 줄의 추가 코드만으로 설명적인 이름을 제공합니다.
Will Byrne

나는 그것을 얻지 못한다. 그는 무엇이든 람다를 전달할 수 있으며 작동합니까? 그가 통과하면 어떻게됩니까 (int a, int b, int c)을 위해 TwoArgIntOperator. 하면 어떻게됩니까 TwoArgIntOperator 같은 서명 방법. 이 답변은 혼란 스럽습니다.
Tomáš Zato-복원 Monica Monica

7
@ TomášZato : 일치하지 않는 인수와 함께 람다를 사용하면 컴파일러가 불평합니다. 또한 기능 인터페이스 만 사용할 수 있으므로 두 가지 (기본이 아닌) 메소드가있는 인터페이스 는 람다로 사용할 수 없습니다 .
Joachim Sauer

기본 질문 : 나는 여전히 메소드의 매개 변수가 누락 된 매개 변수로 메소드를 전달하는 측면을 이해하지 못한다고 생각합니다. 그가 TwoArgIntOperator를 매개 변수로 전달하고 해당 메소드의 매개 변수를 별도로 전달 해야하는 경우 추악하지 않습니까? 매개 변수와 함께 완전한 실행 본문을 전달하는 방법이 있습니까? 예와 같이 "5"및 "10"하드 코딩을 피하는 방법.
instanceOfObject

63

Lambda 표현식을 사용하려면 고유 한 기능 인터페이스를 작성하거나 두 개의 정수가 필요하고 값으로 리턴되는 조작에 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));

    }
}

내가 만든 다른 예는 여기


IntBinaryOperator설명서에 대한 링크가 작동하지 않습니다.
Hendrikto

1
이것은 내가 지금까지 찾은 람다의 가장 좋은 예이며, 실제로 마침내 그것을 얻었습니다.
JimmySmithJR

1
Sooooooo ... 기본적으로 대리인이지만 우리는 그것을 그렇게 부르지 않아야합니까?
Ryan Lundy

37

두 개 이상의 매개 변수가없는 함수의 경우 고유 한 인터페이스를 정의하지 않고 전달할 수 있습니다. 예를 들어

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에서 이미 사용 가능한 모든 인터페이스는 이 페이지 를 참조하십시오 .


15

http://lambdafaq.org/lambda-resources 에서 링크 된 공개 웹 액세스 버전의 Lambda 지원 Java 8 JavaDoc이 있습니다. 있습니다. (이것은 분명히 Joachim Sauer의 답변에 대한 의견이어야하지만 의견을 추가 해야하는 평판 포인트로 내 SO 계정에 들어갈 수는 없습니다.) lambdafaq 사이트 (유지)는 이것과 다른 많은 Java에 대답합니다. 람다 질문.

NB이 답변은 Java 8 GA 문서가 공개 되기 전에 작성되었습니다 . 그래도 Lambda FAQ 는 Java 8에 도입 된 기능에 대해 배우는 사람들에게 유용 할 수 있기 때문에 그대로 두었습니다 .


2
링크와 해당 사이트를 유지 관리한다는 사실에 감사드립니다! 나는 당신의 공개 JavaDoc에 대한 링크를 내 대답에 추가하기 위해 자유를 얻었습니다.
Joachim Sauer

1
참고로 : Angelika Langer가 Generics 용으로 구축 한 것을 Lambdas 용으로 빌드하는 것 같습니다 . 고마워, 자바는 그런 자원이 필요하다!
Joachim Sauer

1
이 링크가 질문에 대한 답변을 제공 할 수 있지만 여기에 답변의 필수 부분을 포함시키고 참조 용 링크를 제공하는 것이 좋습니다. 링크 된 페이지가 변경되면 링크 전용 답변이 유효하지 않을 수 있습니다. - 리뷰에서
ClearLogic

@ClearLogic 예, 동의했습니다. AFAIR 기존 답변에 아무것도 추가하고 싶지 않았지만 API 문서 사본을 게시 한 위치를 지적하기 만했지만 당시에는 쉽게 액세스 할 수 없었습니다.
Maurice Naftalin

7

나에게 가장 의미있는 해결책은 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구현하는 데 사용할 수있는 바로 가기 구문의 이름입니다.


6

인터넷 검색을하는 사람에게는을 사용하는 것이 좋습니다 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


1
대신에 Consumer<Pair<A,B>>, 사용 BiConsumer<A,B>이 경우에. ( docs )
nobar

그것이 존재하는지 몰랐다. 다음에 함수 패키지를 살펴보아야한다.
Big_Bad_E

5

람다 식을 인수로 전달할 수 있습니다. 람다 식을 인수로 전달하려면 람다 식을 인수로받는 매개 변수의 유형이 기능적인 인터페이스 유형이어야합니다.

기능적인 인터페이스가있는 경우-

interface IMyFunc {
   boolean test(int num);
}

그리고 int가 5보다 큰 경우에만 int를 목록에 추가하는 필터 메소드가 있습니다. 여기서 필터 메소드에는 매개 변수 중 하나로 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);
    }
}

2

위에서 언급 한대로 기능 인터페이스를 사용할 수 있습니다. 아래는 몇 가지 예입니다

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

그것이 도움이되기를 바랍니다.


1

글쎄, 그건 쉽다. 람다 표현의 목적은 기능 인터페이스를 구현하는 것입니다. 하나의 메소드 만있는 인터페이스입니다.다음은 사전 정의 된 기능 인터페이스와 레거시 기능 인터페이스에 대한 놀라운 기사입니다.

어쨌든, 자신의 기능적 인터페이스를 구현하려면 그것을 만드십시오. 간단한 예를 들면 다음과 같습니다.

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

그게 다야!


1

Lambda는 객체가 아니라 기능 인터페이스입니다. @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

Lambda Expression의 기본 개념은 고유 한 논리를 정의하지만 이미 정의 된 인수를 정의하는 것입니다. 위의 코드에서 do 함수의 정의를 다른 정의에서 추가로 변경할 수 있지만 인수는 2로 제한됩니다.


1

기본적으로 lamda 표현식을 매개 변수로 전달하려면이를 보유 할 수있는 유형이 필요합니다. 정수 값과 마찬가지로 기본 int 또는 Integer 클래스에 보유합니다. Java에는 lamda 표현식에 대해 별도의 유형이 없으며 대신 인터페이스를 유형으로 사용하여 인수를 보유합니다. 그러나이 인터페이스는 기능적인 인터페이스 여야합니다 .


0

다음을 수행하십시오 ..

당신은 선언 한 method(lambda l) 당신이 이름을 가진 인터페이스를 만들 수 있습니다 싶지 모두 lambda하나 개의 추상 메소드를 선언

public int add(int a,int b);  

메소드 이름은 여기서 중요하지 않습니다.

그래서 u는 호출 할 때 MyClass.method( (a,b)->a+b) 이 구현은 (a,b)->a+b전화 할 때마다 인터페이스 추가 방법 낭포에 주입 될 것이다 l.add는이 구현을 가지고의 추가를 수행 할 것입니다 abreturn l.add(2,3)반환합니다 5. -기본적으로 이것은 람다가하는 일입니다.


-1

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

-2

람다를 매개 변수로 사용하면 유연성이 있습니다. 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)
        }

    }

F 인터페이스 fi = (x)-> {return 0; };

funcUsesAnonymousOrLambda (fi);

여기에서 람다식이 인터페이스로 대체되는 방법을 볼 수 있습니다.

위의 람다 식의 특정 사용법을 설명하면 더 있습니다. 심판 람다 내에서 자바 8 람다 외부 람다에서 변수를 수정할 수 없습니다

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.