자바 인터페이스

Tina Jeong·2021년 2월 8일
0

Re-자바

목록 보기
12/16

자바 상속 포스팅에서도 인터페이스를 정리했지만, 오늘은 해당 내용을 다시 자세하게 정리할 예정이다. 추상 클래스와의 비교는 이전 포스팅에서 자세히 정리했으므로 여기서는 언급하지 않는다.

인터페이스 정의


인터페이스는 일종의 specification 같다는 생각이 든다. 아래는 폭스바겐의 자동차 라인 중 하나인 T-Roc의 스펙인데 변속기 타입에 따라 차를 작동시키는 방식이 달라지지만 모든 자동차는 변속기 타입을 가지니 공통된 액션과 요구사항을 리스트업할 수 있을 것이다.

자바의 인터페이스는 interface 키워드와 함께 정의하며, 상속 받을 객체가 수행할 액션을 메소드 signature로 명시해둔다. 해당 인터페이스를 상속 받는 객체는 반드시 해당 메소드를 구현해야 한다.

public interface Relatable {
    final static int smaller =-1;
    final static int equal =0;
    final static int larger = 1;
    // this (object calling isLargerThan)
    // and other must be instances of 
    // the same class returns 1, 0, -1 
    // if this is greater than, 
    // equal to, or less than other
    public int isLargerThan(Relatable other);
}

Point 1 인터페이스의 접근지시자

public interface InterfaceSample {
...
}

인터페이스에는 접근 지시자 중 public 키워드만 선택적으로 사용할 수 있으며, public 키워드를 붙이지 않으면 default이므로 package 내부적으로 접근가능한 인터페이스가 된다. 인터페이스는 객체간 행동 방식을 통일하기 위한 상속이 목적이니 private interface는 당연히 허용하지 않는다. private은 인터페이스의 태생을 부정하는 키워드였다고..

Point 2 extends vs implements

public interface InterfaceSample extends Interface1, Interface2, Interface3 {
...
}

자바는 다중상속을 허용하지 않기 때문에 하나의 클래스만 extends할 수 있지만 인터페이스는 여러개의 인터페이스를 extends할 수 있다. 그런 의미에서 인터페이스가 인터페이스를 implements하는 경우는 거의 없다. 왜냐하면 implements는 상속받은 인터페이스의 구현을 암시하는 키워드이기 때문이다.

Point 3 et cetera

인터페이스는 기본적으로 메소드 시그니처와 constant만 존재한다. 자바 8부터는 default, static 메소드가 추가되었고, 자바 9부터는 private 메소드가 추가 되었다.

인터페이스 구현


인터페이스의 자식 클래스나 인터페이스는 implements 키워드를 통해 인터페이스의 메소드를 구현한다.

public class RectanglePlus implements Relatable {
...
	@Override
    public int isLargerThan(Relatable other) {
        RectanglePlus otherRect 
            = (RectanglePlus)other;
        if (this.getArea() < otherRect.getArea())
            return -1;
        else if (this.getArea() > otherRect.getArea())
            return 1;
        else
            return 0;               
    }
}
public class Subclass extends Superclass implements Interface1, Interface2 {
...
}

implements 키워드는 보통 extends 키워드 뒤에 쓰이는 것이 일반적이며, 인터페이스는 다중 상속이 가능하므로 아래와 같이 쉼표로 여러개의 인터페이스를 상속받을 수도 있다.

인터페이스 레퍼런스를 통해 구현체를 사용하는 방법

인터페이스도 타입이다. 그리고 인터페이스를 상속받은 구현체 클래스들은 자식 클래스이다. 그래서 최상위 객체인 Object를 인터페이스인 Relatable로 타입 캐스팅해서 자식 클래스들을 사용할 수 있다. isLargerThan()의 구현방식은 조금씩 다르지만, 시그니처는 동일하므로 findLargest()에서 한번의 구현만으로 동일한 작동을 기대할 수 있다.

public Object findLargest(Object object1, Object object2) {
   Relatable obj1 = (Relatable)object1;
   Relatable obj2 = (Relatable)object2;
   if ((obj1).isLargerThan(obj2) > 0)
      return object1;
   else 
      return object2;
}

default 메소드

from java 8


default 키워드를 메소드 시그니처 맨앞에 붙여서 사용한다. default 메소드는 interface에 메소드 구현로직이 존재할 수 있도록 만들어준 메소드이다. default 메소드는 public이므로 interface를 상속받는 자식 클래스들이 해당 메소드를 바로 사용할 수 있으며, overriding을 강요하지 않기 때문에 interface에 구현을 하는 것이 일관성있고 안전하다고 여겨지면 사용한다.

interface Interface1 {
    default void printCurrentTime() {
        System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.KOREA)
                .format(System.currentTimeMillis()));
    }

}

주의할 점은, 인터페이스가 다중 상속을 지원하기 때문에 발생하는 Diamond Problem이다. 독립된 두개의 인터페이스가 동일한 이름의 메소드를 가지고, 자식 클래스가 해당 인터페이스 두개 모두 상속 받는 경우를 떠올려보자. 자식 클래스는 두개의 인터페이스 중 어떤 메소드를 호출해야될지 참 애매하다. 이럴 땐, 반드시 자식 클래스에서 overriding을 해줘야 한다. 부모 인터페이스의 메소드를 호출해서 사용할 순 있어도.

static 메소드

from java 8


static 키워드를 메소드 시그니처 맨 앞에 붙여서 사용한다. default와 마찬가지로 자식 클래스가 개별적인 구현을 할 필요가 없는 경우에 사용한다. 다만 static 메소드는 static이니까 interface에 귀속되는 메소드임을 기억한다.

interface Interface1 {
    default void printCurrentTime() {
        System.out.println(getCurrentTime());
    }
    static String getCurrentTime() {
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.KOREA)
                .format(System.currentTimeMillis());
    }
}

private 메소드

from java 9


private 메소드도 default, static 메소드 처럼 cohesion 향상을 위해 쓰인다. 자식 클래스가 private 메소드의 내용을 알 필요도 없고 overriding도 필요하지 않은 경우에 사용한다. 다만 private 메소드는 default, static 메소드와 달리 java 9부터 사용가능함을 기억한다.

interface Interface1 {
    default void printCurrentTime() {
        System.out.println(getCurrentTime());
    }
    private static String getCurrentTime() {
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.KOREA)
                .format(System.currentTimeMillis());
    }
}

참고
https://docs.oracle.com/javase/tutorial/java/IandI/index.html
https://docs.oracle.com/javase/tutorial/java/IandI/interfaceDef.html
https://docs.oracle.com/javase/tutorial/java/IandI/usinginterface.html
https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html
https://www.baeldung.com/java-static-default-methods
https://www.geeksforgeeks.org/private-methods-java-9-interfaces/
https://www.cardekho.com/volkswagen/t-roc/specs#leadForm

계속해서 문서를 업데이트하고 있습니다. 언제든지 댓글피드백 남겨주세요. 😉

profile
Keep exploring, 계속 탐색하세요.

0개의 댓글