Justin의 개발 로그
article thumbnail

람다 : 익명 함수

람다식 또는 람다함수는 프로그래밍 언어에서 사용되는 개념으로 익명함수(匿名函數, Anonymous functions)를 지칭한다.

 

함수형 인터페이스 

하나의 추상 메서드만 선언된 인터페이스

람다 표현식으로 함수형 인터페이스의 추상 메서드 구현을 직접 전달할 수 있다.

람다 표현식 = 함수형 인터페이스의 추상 메서드 코드 전달

 

 

그렇다면 왜 아래 Comparator<T> 인터페이스는 함수형 인터페이스인가?

@FunctionalInterface

Comparator<T> {

  int compare(T o1, T o2);

  boolean equals(Object obj);

}

Java의 모든 참조형 객체는 Object 클래스의 equals, getClass, hasCode, toString ... 메서드를 사용할 수 있다.

즉, Comparator의 equals 메서드는 Java 최상위 클래스인 Object 클래스에서 제공되는 메서드로 추상 메서드가 아닌 super class의 메소드를 override 하는 개념이며, Comparator의 추상 메서드는 여전히 compare 하나 뿐이다.

 

 

함수형 인터페이스의 구현

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

익명 클래스 방식 함수형 인터페이스 Runnable을 구현하는 기존(Java 8 이전)의 

Runnable r2 = new Runnable() {
  public void run() {
    System.out.println("Hello World 2");
  }
}

람다 표현식 (변수 할당 방식)

Runnable r1 = () -> System.out.println("Hello World 1");

 

활용 예)

public static void process(Runnable r) {
  r.run();
}

@Test
void Test() {
  process(r1);
  process(r2);
  process( () -> System.out.println("Hello World 3") ); //직접 전달된 람다 표현식

 

함수형 인터페이스를 인수로 받는 메서드에만 람다 표현식을 사용할 수 있다. 

왜 그런 결정을 했을까?

  • 설계자들은 언어를 더 복잡하게 만들지 않는 방법을 선택했다.
  • 대부분의 자바 프로그래머가 하나의 추상 메서드를 갖는 인터페이스에 이미 익숙하다는 점도 고려되었다.

 

@FunctionalInterface 어노테이션의 역할은?

@FunctionalInterface는 함수형 인터페이스임을 가르키는 어노테이션이다.
이 어노테이션이 선언된 인터페이스는 추상 메서드가 하나여야 하며, 둘 이상의 추상 메서드가 있으면 
"Multiple nonoverriding abstract methods found in interface xxx" 같은 에러가 발생할 수 있다.

 

에러에서도 알 수 있듯이 overriding된 추상 메서드는 함수형 인터페이스 조건에 영향을 주지 않는다.

 

[References]docs.oracle.com 공식 문서 및 @FunctionalInterface의 주석에도 언급되어 있다.

https://docs.oracle.com/javase/8/docs/api/java/lang/FunctionalInterface.html

* If an interface declares an abstract method overriding one of the public methods of java.lang.Object, that also does not count

인터페이스 타입 선언이 Java 언어 사양에 의해 정의된 것처럼 함수형 인터페이스가 되도록 의도되었음을 나타내는데 사용되는 정보성 주석 유형입니다. 개념적으로 함수형 인터페이스에는 정확히 하나의 추상 메서드가 있습니다. 기본(Default) 메서드에는 구현이 있으므로 추상 메서드가 아닙니다. 인터페이스가 java.lang.Object의 공용 메서드 중 하나를 재정의하는 추상 메서드를 선언하는 경우 인터페이스의 모든 구현이 java.lang.Object 또는 다른 곳(클래스 or 인터페이스)에서 구현되기 때문에 인터페이스의 추상 메서드 수에 포함되지 않습니다.
함수형 인터페이스의 인스턴스들은 람다표현식, 메소드 참조, 생성자 참조로 만들어질 수 있습니다.

이 어노테이션(@FunctionalAnnotation)이 주석으로 붙은 타입은 컴파일 단계에서 다음 요구사항을 충족하지 않으면 에러 메시지를 발생시킵니다.

 

    • 유형은 인터페이스 유형이어야 하며, 어노테이션 유형, 열거형 또는 클래스가 아니어야 합니다.
    • 주석이 달린 유형은 함수형 인터페이스의 요구 사항을 충족해야 합니다.

그렇지만 컴파일러는 인터페이스 선언에 FunctionalInterface 주석이 있는지 여부에 관계없이 기능적 인터페이스의 정의를 충족하는 모든 인터페이스를 기능적 인터페이스로 처리합니다.

1.8부터

 

 

하나 하나 주옥같은 주석이지만, 요약하면...

- 함수형 인터페이스는 하나의 추상 메서드만 존재해야 한다.
- Object 클래스의 equals 메소드는 추상 메소드가 아니기 때문에 인터페이스에서 추상 메서드로 선언하더라도 추상 메서드 수에 포함되지 않는다.
- @FunctionalInterface는 인터페이스가 함수형 인터페이스 조건을 충족하는지 컴파일 단계에서 체크한다.
- 그렇지만, 이 어노테이션이 없어도 조건만 충족하면 함수형 인터페이스다.
- 함수형 인터페이스는 람다식, 메소드 참조, 생성자 참조로 만들어 질 수 있다.

 

람다 표현식

inventory.sort(

   (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())

);

 

메서드 참조

inventory.sort(compareTo(Apple::getWeight)); 

 

생성자 참조

ClassName::new 처럼 클래스명과 new 키워드를 이용해서 기존 생성자의 참조를 만들 수 있다. 

생성자에만 적용된다. (생성자가 아닌 메소드에서 일치하는 람다 표현식이 발견되어도 사용에 지장이 없다.)

 

인자가 없는 생성자 : Suplier<T> :  () -> T

인자가 하나인 생성자 : Function<T, R> : (T) -> R

인자가 두 개인 생성자 : BiFunction <T, U, R> : (T, U) -> R

 

        Supplier<Apple> appleFactory = Apple::new;
        Apple a1 = appleFactory.get();

        System.out.println(a1);

인자가 세 개 이상인 생성자 : 함수형 인터페이스를 정의해서 사용해야 함.

public interface TriFunction<T, U, V, R> {
	R apply(T t, U u, V u);
}

 

profile

Justin의 개발 로그

@라이프노트

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!