코린이의 소소한 공부노트

함수형 인터페이스의 정의와 활용 본문

Java

함수형 인터페이스의 정의와 활용

무지맘 2022. 12. 29. 01:28

[함수형 인터페이스]

1. 정의: 단 하나의 추상 메서드만 선언된 인터페이스

@FunctionalInterface // 생략할 수 있지만 이 애너테이션을 달아두면 컴파일러가 확인해줌
interface MyFunction { // 인터페이스의 추상 메서드는 모두 public abstract
    public abstract int max(int a, int b); // 제어자 생략 가능
    public abstract int max2(int a, int b); // 함수형 인터페이스의 추상 메서드는 1개여야 하기 때문에
    					// 애너테이션을 달면 에러 발생. 달지 않으면 에러X
}

// MyFunction 구현
MyFunction f = new MyFunction(){ // 익명 클래스의 선언 + 객체 생성
    public int max(int a, int b){ return a>b ? a : b; } // public을 생략하면 에러 발생
};
int value = f.max(3,5); // OK. MyFunction 인터페이스에는 max()가 있음

2. 함수형 인터페이스는 람다식을 다루기 위한 도구이다.

  - 즉, 람다식의 참조변수는 함수형 인터페이스 타입이라는 것이다.

  - , 함수형 인터페이스의 메서드와 람다식의 매개변수 개수와 반환타입이 일치해야 한다.

MyFunction f = (a, b) -> a>b ? a : b;
// 인터페이스의 max()는 매개변수 2개, 반환타입은 int였다.

int value = f.max(3,5); // 람다식에는 이름이 없지만 실제로는 람다식이 호출된다.

 

[함수형 인터페이스의 활용]

1. 람다식을 메서드의 매개변수로 사용할 수 있다.

@FunctionalInterface
interface MyFunction {
    void myMethod();
}

void aMethod(MyFunction f) { // 매개변수로 람다식 가능
    f.myMethod(); // MyFunction에 정의된 메서드 호출
}

// 람다식 호출
MyFunction f = () -> System.out.println("myMethod()");
aMethod(f);

// 위의 두 줄을 한 줄로 합친 것
aMethod(() -> System.out.println("myMethod()"));

2. 람다식을 반환타입으로도 사용할 수 있다.

MyFunction myMethod() { // 람다식을 반환하는 메서드
    MyFunction f = ()-> { };
    return f;
    // return ()-> { }; // 위의 두 줄을 한줄로 합친 것
}

3. 예제 1 - 문자열 역순 정렬

@FunctionalInterface
interface Comparator<T> {
    int compare(T o1, T o2); // o1>o2일때 양수 반환
}
 
// 정렬 대상
List<String> list = Arrays.asList("abc", "aaa", "bbb", "ddd", "aaa");
 
// 1) 익명 객체(클래스) 선언 + Comparator 구현
Collections.sort(list, new Comparator<String>() {
                public int compare(String s1, String s2) {
                        return s2.compareTo(s1); // 거꾸로 비교
                }
            });
 
// 2) 람다식 이용
Collections.sort(list,(s1,s2)-> s2.compareTo(s1));

// 결과: list = [ddd, bbb, abc, aaa, aaa]

4. 예제 2 - 람다식의 여러 가지 사용 방법

@FunctionalInterface // 함수형 인터페이스는 추상 메서드가 1개
interface MyFunction {
    void run();  // public abstract이 생략된 형태
}

// 테스트 클래스 시작
// 클래스 메서드 1
static void execute(MyFunction f) { // 매개변수의 타입이 MyFunction인 메서드
    f.run();
}

// 클래스 메서드 2
static MyFunction getMyFunction() { // 반환 타입이 MyFunction인 메서드 
    MyFunction f = () -> System.out.println("f3.run()");
    return f;
}

// main 메서드 시작
// 1) 람다식으로 MyFunction의 run() 구현
MyFunction f1 = () -> System.out.println("f1.run()");

// 2) 익명 클래스로 run() 구현
MyFunction f2 = new MyFunction() {  // 익명클래스로 run()을 구현
    public void run() {   // public을 반드시 붙여야 함
        System.out.println("f2.run()");
    }
};

// 3) 클래스의 static 메서드로 run() 구현
MyFunction f3 = getMyFunction();

// 다음 다섯 줄의 코드를 실행 결과는 맨 아래에 있다.
f1.run();
f2.run();
f3.run();
execute(f1);
execute( ()-> System.out.println("run()") );
// main 끝
// 테스트 클래스 끝

// 실행 결과
f1.run()
f2.run()
f3.run()
f1.run()
run()