언어/Java

람다와 함수형 인터페이스

honeyricecake 2023. 2. 7. 19:12

앞의 람다의 소개에서 봤던 것을 대충 정리하자면

A -> B 가 람다식이라 할 때

-> 는 연산자이고 ->의 왼쪽에 매개변수에 대한 선언정보, 오른쪽에 람다식으로 표현하고자 하는 연산문이 들어오게 된다.

 

함수형 인터페이스란?

 

인터페이스 중 구현해야할 추상 메소드가 딱 하나 있는 인터페이스를 가리켜 함수형 인터페이스라고 한다.

우리가 앞의 예시에서 봤듯이

우리가 람다식을 작성하는 대상이 바로 함수형 인터페이스이다.

 

람다는 인스턴스보다 기능 하나가 필요한 상황을 위하여 있다.

 

앞에서 보았던

Collections.sort(list, new SLenComp());  // 정렬

과 같이 두번째 인자로 필요한 것은 사실 compare 메소드이나

메소드를 인자로 주는 것이 불가능하기 때문에 인스턴스를 생성하여 두번째 인자로 전달해주게 된다.

 

이런 경우 람다식이 인스턴스 생성을 대신 하게 될 수 있다.

 

 

매개변수가 있고 반환하지 않는 람다식

 

ex.

interface Printable {
	void print(String s);  // 매개변수 하나, 반환형 void
}

class OneParamNoReturn {
	public static void main(String[] args)
    {
    	Printable p;
        p = (String s) -> {System.out.println(s)};  // 줄임없는 표현
        p.print("Hello World");
        
        p = (String s) -> System.out.println(s);  // 중괄호 생략
        p.print("Hello World");
        
        p = (s) -> System.out.println(s); // 매개변수 형 생략
        p.print("Hello World");
        
        p = s->System.out.println(s);  // 매개변수 소괄호 생략
        p.print("Hello World");
    }
}

메소드 몸체가 둘 이상의 문장으로 이루어져 있으면 메소드의 표현인지 다른 식인지 확실하게 해주어야 하므로 중괄호의 생략이 불가능하다.

매개변수의 수가 둘 이상인 경우에도 소괄호의 생략이 불가능하다.

 

즉 일반적으로는

p = (a, b...) -> { ~; ~; ~;};

이와 같은 표현이 맞다.

 

 

매개변수가 둘인 람다식의 예시

 

interface Calculate {
	void cal(int a, int b);
}

class TwoParamNoReturn {
	public static void main(String[] args) {
    	Calculate c;
        c = (a, b) -> System.out.println(a + b);
        c.cal(4, 3);
    }
}

 

매개변수가 있고 반환하는 람다식

 

interface Calculate {
	int cal(int a, int b);
}

class TwoParamAndReturn {
	public static void main(String[] args) {
    	Calculate c;
        c = (a, b) -> {return a + b;};  // return문의 중괄호 생략은 불가
      	System.out.println(c.cal(4,3));
        
        c = (a, b) -> a + b;  // 연산 결과가 남으면 별도로 명시하지 않아도 반환대상이 됨
        System.out.println(c.cal(4,3));
    }
}

return 문을 쓰면 중괄호는 생략 불가능하다.

그냥 return 하는건지 람다식인지 구분이 필요하기도 하고
return문은 문장의 시작 부분에 위치한다는 규칙을 깨지 않는 것이 컴파일러 설계 상 좋기 때문이기도 하다.

 

그리고 마지막 문장 실행결과 연산 결과가 남으면 return을 명시하지 않아도 그 값을 리턴한다.

 

 

매개변수가 없는 람다식

 

interface Generator {
	int rand();
}

class NoParamAndReturn {
	public static void main(String[] args) {
    	Generator gen = () -> {
        	Random rand = new Random();
            return rand.nextInt(50);  // 0 ~ 49의 숫자 중 하나 랜덤으로 리턴
        }
        
        System.out.println(get.rand());
    }
}

 

 

함수형 인터페이스(Functional Interface)와 어노테이션

 

함수형 인터페이스: 추상 메소드가 딱 하나만 존재하는 인터페이스

 

@FunctionalInterface : 함수형 인터페이스의 조건을 갖추었는지에 대한 검사를 컴파일러에게 요청!

 

@FunctionalInterface
interface Calculate {
	int cal(int a, int b);
}

@FunctionalInterface
interface Calculate {
	int cal(int a, b);
    default int add(int a, int b) {return a + b;}
    static int sub(int a, int b) {return a - b:}
}

 

위의 두 인터페이스는 모두 함수형 인터페이스의 조건을 갖추었다.

둘 다 추상 메소드가 하나이기 때문이다.

 

 

람다식과 제네릭

 

 

@FunctionalInterface
interface Calculate <T> {
	T cal(T a, T b);
}

class LambdaGeneric {
	public static void main(String[] args) {
    	Calculate<Integer> ci = (a, b) -> a + b;
        System.out.println(ci.cal(4, 3));
    }
}

인터페이스가 제네릭 기반이라 하여 특별히 신경쓸 필요는 없다.

타입인자가 전달이 되면 추상 메소드의 T는 결정이 되므로!

(https://honeyricecake-webdev.tistory.com/29 의 4.다이아몬드 기호를 참고하자.)