[데브코스] Interface 이야기
Updated: Categories: TILW1D3 - 어떻게 해야 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);
}