선언된 추상매소드가 단 하나만 존재하는(구현매소드는 상관없음) 인터페이스(or 추상클래스)를 별도의 클래스 선언으로 확장하지 않고 코드부에서 바로 구현하는 기술입니다.
아래 코드를 봅시다.
...
Thread t = new Thread(new Runnable() {
public void run() {
System.out.println("익명클래스 구현부");
}
});
t.start();
...
위와 같이 스레드 객체를 생성할때 Thread 인스턴스 생성자 파라미터로 할당하는 new Runnable()....
이 바로 익명클래스입니다.
이 선언은 어떻게 동작하는 건지 알아보기 위해 아래 Thread 생성자와 Runnable에 대해 자세히 알아봅시다.
package java.lang;
...
class Thread implements Runnable {
...
public Thread(Runnable target) {
this(null, target, "Thread-" + nextThreadNum(), 0);
}
}
package java.lang;
...
@FunctionalInterface
// @FunctionlaInterface 어노테이션은 아래 함수형 인터페이스에서 다시 다루겠습니다.
public interface Runnable {
public abstract void run();
}
Thread(Runnable target) 는 Runnable 인터페이스 구현체를 생성자 파라미터로 받습니다.
Runnable 인터페이스는 run() 이라는 추상 매소드 하나만 선언되어 있으므로 익명클래스로 구현이 가능하므로 아래와 같이 Runnable 인터페이스의 구현체 코드부에서 바로 생성하여 생성자로 전달합니다.
(new Runnable() {
public void run() {
System.out.println("익명클래스 구현부");
}
})
Runnable 인터페이스implements 하는 클래스를 별도 정의하여, 새로 정의한 클래스의 인스턴스를 생성자로 넘겨주어도 동일하게 동작합니다.
인터페이스를 사용하는 목적이 단일 기능에 대한 정의(단 하나의 추상매소드)이고 특정 인터페이스 구현이 한번만 사용되어 재사용이 불필요한 경우,
클래스 선언 비용을 줄이고 코드 간결화를 위해 익명클래스를 사용합니다.
익명클래스는 자바의 기존 객체지향적 패러다임 보다는 함수형프로그래밍 패러다임에 적합한 방식이라 볼 수 있습니다.
익명클래스는 클래스를 별도 선언하지 않아도 된다는 점에서 기존보다 코드가 간결해졌지만, 여전히 함수형 프로그래밍을 지향하는 타 언어들에 비하면 작성해주어야할 부분이 많습니다.
위 예시에서 익명클래스로 Thread를 생성했던 부분을 Lambda식을 이용해 바꿔보겠습니다.
...
Thread t = new Thread(()->System.out.println("lambda로 Runnable 구현체 정의"));
t.start();
...
구문이 훨씬 간결해진것을 볼 수 있습니다.
Lambda식으로의 변형은 인터페이스에 @FunctionalInterface 어노테이션을 붙여주어야 가능합니다. @FunctionalInterface는 SAM 특성을 제약하여 SAM에 어긋나면 컴파일 타임에 오류를 체크할 수 있습니다.
SAM(Single Abstract Method)?
SAM은 추상 매소드가 단 하나인 인터페이스의 특성을 나타내는 것으로 SAM을 만족하는 인터페이스만 익명클래스, 람다표현식으로 구현될 수 있습니다.