JAVA - 익명 클래스와 람다 표현식

choko's avatar
Jun 29, 2024
JAVA - 익명 클래스와 람다 표현식
 
 

익명 클래스

  • 이름이 없는 클래스, 보통 일시적으로 한번만 사용되고 버려지는 객체이다.
  • 따로 클래스 정의 없이 메서드 내에서 바로 클래스를 생성해 인스턴스화 할 수 있으며, 단 한 번만 사용되고 버려진다.
  • 부모 클래스의 자원을 상속받아 재정의하여 사용할 자식 클래스가 한번만 사용되고 버려질 상황이라면, 익명 클래스를 사용하는게 효과적이다.
 
// 부모 클래스 class Animal { public String bark() { return "동물이 웁니다"; } } public class Main { public static void main(String[] args) { // 익명 클래스 : 클래스 정의와 객체화를 동시에. 일회성으로 사용 Animal dog = new Animal() { @Override public String bark() { return "개가 짖습니다"; } }; // 단 익명 클래스는 끝에 세미콜론을 반드시 붙여 주어야 한다. // 익명 클래스 객체 사용 dog.bark(); } }
 

유의점

  • 기존 부모 클래스를 상속한 자식 클래스의 경우, 부모 클래스에 없는 새로운 메서드를 만들어 사용할 수 있다.
  • But, 익명 클래스 방식으로 선언하면, 오버라이딩 한 메서드만 사용 가능하고 새로 정의한 메서드는 외부에서 사용이 불가능하다.
// 부모 클래스 class Animal { public String bark() { return "동물이 웁니다"; } } public class Main { public static void main(String[] args) { Animal dog = new Animal() { // @Override 메소드 public String bark() { return "개가 짖습니다"; } // 새로 정의한 메소드 public String run() { return "달리기 ㄱㄱ싱"; } }; dog.bark(); dog.run(); // ! Error - 외부에서 호출 불가능 } }
  • new Animal() 를 통해 생성하는 인스턴스는 별도의 클래스가 아닌, Animal 클래스를 상속받는 익명 클래스이기 때문이다. 따라서 Animal에 없는 run() 메서드는 외부에서 호출이 불가능하고 익명 클래스 내에서만 호출할 수 있다.
 
 
 

인터페이스 익명 구현 객체

  • 인터페이스를 일회용으로 구현하여 사용할 필요가 있을 때, 익명 구현 객체로 선언해서 사용
// 인터페이스 interface IAnimal { public String bark(); // 추상 메소드 public String run(); } public class Main { public static void main(String[] args) { // 인터페이스 익명 구현 객체 생성 IAnimal dog = new IAnimal() { @Override public String bark() { return "개가 짖습니다"; } @Override public String run() { return "개가 달립니다"; } }; // 인터페이스 구현 객체 사용 dog.bark(); dog.run(); } }
  • 본래 클래스가 인터페이스 구현 후 객체를 만들어야 하지만, 바로 클래스명 없이 객체를 만들 수 있다.
    • 이를 익명 구현 객체라고 부른다.
    • Thread 생성 시 Runnable도 이런 방식을 사용한다.
  • 한계점
    • 오직 하나의 인터페이스만 구현하여 객체를 생성할 수 있다.
 
 

익명 객체와 람다 표현식, 함수형 인터페이스

  • 익명 클래스 기법을 java8의 람다식 문법과 같이 쓰면 매우 효과적이다.
@FunctionalInterface // -> 추상 메서드가 하나인지 검사하여, 함수형 인터페이스가 맞는지 check public interface Operate { int operate(int a, int b); // 인터페이스의 모든 메소드는 예외없이 public이면서 abstact이기에 생략 가능 // default 메서드는 추상 메서드에 포함되지 않는다 default void print() { System.out.println("출력"); } } Operate operate = new Operate() { public int operate(int a, int b) { return a + b; } }; // 람다식으로 줄이기 Operate operate = (a, b) -> { return a + b; }; // 더 짧게 줄이기 (리턴 코드만 있다면 생략이 가능) Operate operate = (a, b) -> a + b;
  • 이러한 람다식 표현의 익명 구현 객체는 2가지 제약이 있다.
    • 인터페이스로만 만들 수 있다.
    • 하나의 추상 메서드만 선언되어 있는 인터페이스만 가능하다. → 함수형 인터페이스라고 한다.
    • 두 개 이상의 추상 메서드가 정의되어 있는 인터페이스의 경우 사용 불가능
 

람다식의 타입 추론

public ContextV1(Strategy strategy) { this.strategy = strategy; } ContextV1 contextV1 = new ContextV1( () -> log.info("비즈니스 로직 1 실행")); contextV1.execute();
  • ContextV1는 Strategy 타입을 받아야 한다. 그런데, 람다식에서는 Strategy 타입에 대한 명시가 없다.
  • 하지만 이 코드는 동작한다. 컴파일러 스스로 람다 함수식을 보고 추론하여 타입을 유추하기 때문이다.
  • 대부분 함수형 인터페이스는 제네릭을 사용하고, 컴파일러는 대부분 제네릭에서 해당 타입 정보를 판별한다.
 
Share article

Tom의 TIL 정리방