인터페이스

인터페이스는 추상 메서드를 갖기 때문에 추상클래스와 개념이 비슷하다.
하지만 추상 클래스보다 추상화 정도가 높아 클래스의 몸통인 일반 메서드나 멤버 변수를 갖지 않는다.
앞서 추상 클래스를 부분적으로 완성된 미완성 설계도라고 비유했다면, 인터페이스는 구현되는 것도 없는 '기본 설계도'와 같다. 대신, 다른 클래스 작성에 도움을 준다.

인터페이스를 사용하면

  • 추상 메소드만 갖기 때문에 메소드의 선언부만 파악하면 된다. 즉, 개발 시간을 단축시킬 수 있다.
  • 프로젝트의 기본 틀을 인터페이스로 구축하면 코드를 정형화하고 독립적인 클래스로 개발할 수 있다.

추상화 개념과 인터페이스

인터페이스는 클래스와 작성법이 비슷한데, 추상클래스처럼 직접 인스턴스를 생성할 수 없고, 정의된 추상메서드를 클래스의 상속 개념과 유사한 구현을 통해 완성된다.

그러나, 클래스 대신 interface 키워드로 작성되며 완전한 추상화가 구현된다.

클래스와 달리

  • 모든 멤버 변수는 public static final 제어자가 기본이며 생략 가능하다.
  • 모든 메서드는 추상 메서드여야 해 public abstract 제어자가 붙고 역시 생략 가능하다.
  • JDK 1.8부터 static 메서드와 default 메서드가 인터페이스에서 구현될 수 있다.

만일, 인터페이스를 구현할 클래스가 해당 인터페이스의 메서드 중 일부만 구현한다면, 추상 클래스로 선언돼야 해 abstract 제어자가 class 선언 앞에 붙는다.

대신, 인터페이스는 생성자가 없어 인스턴스를 생성할 수 없다.

상속과 인터페이스

앞서 클래스의 상속과 비슷한 개념이 인터페이스에선 구현 implement 이라고 했다.

인터페이스는 인터페이스로만 상속(구현)될 수 있으며, 클래스와 달리 한 번에 여러 인터페이스에서 상속받을 수 있다.

interface Useable {
  void functional (int x, int y);
}
  
interface Variable {
  void oriented (ObjectOrientedLanguage obj);
}
  
interface Startable extends Useable, Variable { };

class JavaScript extends ObjectOrientedLanguage implements Startable {
  public void functional(int x, int y) {....}
  public void oriented (ObjectOrientedLanguage obj) {....}
}

위 예시에서 JavaScript 클래스는

  • Object 클래스에서 상속 받은 ObjectOrientedLanguage 클래스에서 상속받았고,
  • Useable과 Variable 인터페이스를 상속받은 Startable 인터페이스를 구현하고 있다.

위 예시를 정리하며 인터페이스의 두 가지 개념을 이해했는데,

  • 인터페이스는 클래스의 Object 클래스와 달리 상속의 최상위 인터페이스가 따로 존재하지 않고,
  • 특정 인터페이스에 상속 받은 인터페이스나 해당 인터페이스를 구현하는 클래스에선 오버라이딩이 필수적으로 일어나야 하는데, 그렇기 때문에 구현되는 두 메서드는 pulbic이 된다.

왜냐? 메서드 오버라이딩이 구현되려면 부모 클래스의 메서드와 같거나 더 넓은 범위의 접근 제어자가 필요하기 때문이다. 상술했듯, 인터페이스의 메서드는 public abstract 제어자가 생략됐기 때문에, 인터페이스를 구현하는 모든 클래스의 오버라이딩 메서드는 public 접근 제어자 뒤에 위치해야 한다.

앞서 인터페이스는 다중상속이 가능하다고 상술했다. 만약 두 클래스로 상속을 받고 싶은 상황에서 상대적으로 비중이 낮은 클래스를 다른 클래스 내부 멤버로 포함시키거나, 해당 클래스를 차라리 인터페이스로 만드는 방식으로 인터페이스를 활용한 다중 상속을 구현할 수 있다.

설령 해당 클래스와의 충돌이 우려돼도, 인터페이스 내 멤버 변수는 static final 상수로 이뤄지고, 메서드는 추상 메서드이기 때문에 구현 내용이 없어 부모 클래스의 메서드를 상속받게 돼 다중 상속으로 발생될 문제를 자연스레 해결할 수 있다.

다형성과 인터페이스

다형성을 정리할 때 자식 클래스의 인스턴스의 타입을 부모의 것으로 하는 참조 변수를 만들 수 있다고 했다.

인터페이스 역시 이의 자식 클래스의 인스턴스가 인터페이스 타입의 참조 변수로 형변환이 가능하다.

Startable starters = (Startable)new JavaScript();
// 형변환 과정 생략 가능하다.
// 참조 변수 starters는 인터페이스 Startable에 정의된 멤버만 호출이 가능하다.

만약 위 예시처럼 인스턴스 타입의 참조 변수가 매개변수로 사용된다면, 메서드 호출 시 해당 인터페이스를 구현한 클래스의 인스턴스를 매개변수로 제공해야 한다.

그리고 인터페이스 타입으로 지정된 메서드는 인터페이스 타입을 반환하고, 이는 곧 해당 인터페이스를 구현한 클래스의 인스턴스를 반환한다는 의미이다.

디폴트 메서드 & 정적 메서드

앞서 JDK1.8부터 default, static을 사용한 메서드가 인터페이스에 추가될 수 있다고 상술했다. 두 메서드 모두 생략 가능한 public 제어자가 함께 한다.

// default method
interface Writable {
  default void write() {}
}

위와 같이 디폴트 메서드는 기존 추상 메서드와 달리 {} 블럭이 있어 부모 클래스에 새로운 메서드를 추가한 것과 동일해 진다.

만약 기존 메서드와 이름이 충돌한다면,

  • 여러 인터페이스의 디폴트 메서드가 충돌할 때면 구현 클래스에서 오버라이딩해야 한다.
  • 부모 클래스와 충돌이 나면, 디폴트 메서드는 무시된다.
  • 그냥 필요한 곳에서 오버라이딩 하자!

인터페이스는 향후 자바 Collection 인터페이스와 Collection 클래스를 학습할 때 중요한 개념이 될 것이다.


본 후기는 유데미 X 웅진씽크빅 취업 부트캠프 3기 백엔드 과정 학습 일지 리뷰로 작성되었습니다.

유데미 바로가기 / STARTERS 취업 부트캠프 공식 블로그 보러가기


🧷 참고 교재

  • [도우출판]Java의 정석 3rd Edition, - 남궁성
profile
개발이란?

0개의 댓글