[데브코스] Interface 이야기

Updated: Categories:

W1D3 - 어떻게 해야 Interface를 효율적으로 설계할 수 있을까?

Interface

  • 다형성을 활용한 의존성 주입 (Dependency Injection)
  • 의존 역전 (Dependency Inversion) : 객체를 의존할때 추상체를 통해서 의존해라 -> 결합도 낮춤
  • 항상 클래스에 implements 하여 사용해야함

Adapter 클래스

인터페이스의 불편함

public interface MyInterface {
    void method1();
    void method2();
}
  • 메소드를 두개 가지는 인터페이스가 있음.
  • 인터페이스를 상속받은 클래스에서 하나의 메소드만 사용하고 싶어도 억지로 둘다 Overriding 해야함 😡
  • 따라서 미리 Overriding해놓은(그냥 빈 메소드 불러놓은) 클래스가 바로 Adapter 클래스이다!!
  • 인터페이스를 상속받지 않고 Adapter클래스를 상속하면 메소드 하나만 Overriding하면 된다~

하지만

class Main extends Object {
  ...
}
  • 위처럼 이미 다른 클래스를 상속하고 있는 클래스가 있다면.. Adapter 클래스가 낄 자리가 없어진다.
  • 결국 낄 자리가 없으니 implements로 인터페이스를 상속받게됨… => 처음과 똑같은 문제 발생


Default Method

  • Adapter 클래스 멈춰 !! ✋
  • 인터페이스 내부에서 구현체를 가질 수 있게됨 (Overriding 필수 X)
  • 따라서 인터페이스를 상속만 받아도 인터페이스 메소드 호출이 가능..!
  • java 8 이상부터 기능 개선
interface MyInterface {
    // 추상 메소드
    void method1();
    // 구상 메소드 - default method
    default void method2() {
        System.out.println("Hello World");
    }
    // static 메소드도 가질 수 있다.
   static void method3() {};
}
public class Main implements MyInterface{
    // 그냥 호출 가능
    new Main.method2();
    ...
  • 인터페이스가 이미 구현이 된 메소드를 가지니, implements만으로도 Overriding 없이 메소드를 호출할 수 있다!


익명 클래스

public interface MyInterface {
    void method();
}
public class Main {
    MyInterface mi = new MyInterface(){
        @Override
        public void method() {
            ...
        }
    }
}
  • 원래라면 상속받은 클래스를 만들어서 오버라이딩 해야하는데…?
  • 즉석으로 클래스를 만들고 메소드 오버라이딩을 시전. => 엄청나게 효율적


Functional Interface

  • 추상메소드가 하나만 존재하는 인터페이스
  • 인터페이스가 포함하는 메소드가 여러개여도 추상메소드만 하나면 조건 만족
  • Annotation (@FunctionalInterface) 를 달아주어 가독성을 좋게 만들자
  • 어떤 자세한 행위를 할지는 Host Code에서 람다표현식으로 오버라이딩하고 파라미터에 넣어준다.
  • Functional Interface의 종류는 여러가지가 있음. 한번 정리해보자
@FunctionalInterface
interface MyInterface {
    // 메소드가 3개지만 추상메소드는 하나이므로 Functional Interface임
    void method1();
    default void method2() {};
    static void method3() {};
}


Lambda 표현식

  • 익명 메소드를 사용한 간결한 인터페이스 인스턴스 생성 방법
  • Functional Interface를 사용할 때만 사용가능 (애초에 오버라이딩하는 것이므로 당연하다.)
// 변경전
public class Main {
    MyInterface mi = new MyInterface(){
        @Override
        public int method() {
            ...
        }
    }
}
// 변경후
public class Main {
    MyInterface mi = ( ) -> {  };
} 


Method Reference

  • 람다표현식에서 method가 줄줄이 써질때 사용
  • 람다 표현식에서 입력값을 변경없이 바로 사용하는 경우
  • 최종으로 적용될 메소드의 레퍼런스를 지정해줌 -> 입력값을 변경하지말라는 표현 방식! (변경의 여지를 완벽 차단)
// 변경전
public class Main {
    MyInterface mi = ( ) -> System.out.println("HI");
} 
// 변경후
public class Main {
    MyInterface mi = ( ) -> System.out::println;
} 


Generic

  • Primitve type은 제네릭 파라미터로 사용 불가 -> Reference type만 가능!!!
  • 파라미터, 리턴 타입을 유동적으로 지정해야할 때 사용한다.
  • 뭔가 구현시에 <T>를 붙여준다
public interface MyInterface<T> {
    void method(T t);
}