공부/whiteship-java

8주차: 인터페이스

chulphan 2021. 1. 8. 15:41

백기선님께서 주최하시는 온라인 자바 스터디

github.com/whiteship/live-study

 

whiteship/live-study

온라인 스터디. Contribute to whiteship/live-study development by creating an account on GitHub.

github.com

8주차 주제는 인터페이스이다

 

Java 에서 인터페이스는 객체지향 프로그래밍에 주로 쓰이는 개념이다

 

주어진 과제는 다음과 같다

 

■ 인터페이스란??

  • 자바의 참조 타입(Reference Type) 중 하나
  • 구현부가 없는 메소드 즉, 추상 메소드들의 집합 (하지만 자바8 부터는 그렇지 않다. 아래에서 알아볼 것이다)
  • 클래스는 하나만 상속 할 수 있는 반면, 인터페이스는 다중 상속 및 구현이 가능하다
  • 인터페이스 내에 선언된 메소드는 암묵적으로 public 이고 abstract 이다 (그러므로 명시해 줄 필요가 없다)
  • 인터페이스 자체는 객체화를 할 수 없다 (추상 클래스와 마찬가지로)

■ 인터페이스 정의하는 방법

인터페이스는 클래스와 비슷한 방법으로 정의할 수 있다

자바8 보다 이전 버전에서는 인터페이스에 상수(static final) 와 추상 메소드만 포함할 수 있었지만,

자바8 이상에서는 default method, private method(Java9), static method 들도 추가로 선언 & 구현할 수 있다

 

아래 코드는 두 개의 추상 메소드를 포함한 인터페이스이다

interface Hello {
 String Hello();
 String Bye(String name);
}

위의 코드를 보면 알 수 있듯이 메소드에 대한 signature 만 있을 뿐, 이 메소드에 아무런 구현도 되어 있지 않다.

 

※ 인터페이스의 Member 가 되기 위한 조건

  • 인터페이스 내에 필수적으로 구현해야 하는 메소드는 암묵적으로 abstract 로 선언된 것과 같다 (명시 안해줘도 된다)
  • 인터페이스의 멤버들은 암묵적으로 public 으로 선언 되어 있다 (편의상 명시 해주지 않아도 된다)
  • 인터페이스 내에 인스턴스 필드는 정의 될 수 없다. 왜냐하면 필드들은 애초에 인스턴스들의 구현 쪽 영역이고 인터페이스는 명세의 영역이기 때문. 단, 인터페이스 내에 필드를 static final 을 통해 선언 및 정의할 수 있다
  • Nested Type 을 포함할 수 있다. 이 타입도 암묵적으로 public 과 static 이다
  • Java 8버전 부터는 static method 와 default method 를 포함할 수 있다
  • Java 9버전 부터는 private method 를 포함할 수 있다

 

이 메소드를 구현하기 위해서는 클래스에서 이 인터페이스를 구현하면 되는데, 이제 살펴보자

■ 인터페이스 구현하는 방법

클래스의 슈퍼 클래스를 extends 키워드를 사용해서 확장한 것처럼, implements 키워드를 사용함으로써 하나 또는 하나 이상의 인터페이스를 명시하여 구현할 수 있다. 또 extends 절과 implements 절을 같이 사용 할 수 있는데, 이 때는 extends 키워드를 먼저 명시해야 한다

 

implements 를 사용해서 클래스를 선언한다는 것의 의미는 interface 에 명시된 추상 메소드들을 이 클래스 내에서 반드시 구현체를 제공해야 함을 의미한다

 

만약에 이 클래스에서 interface 에 명시된 클래스를 구현하고 싶지 않을 경우에는 abstract 를 명시해서 선언해주면 된다 (상속과 마찬가지로)

 

코드로 클래스가 인터페이스를 어떻게 구현하는지 살펴보자

 

먼저 Car 라는 인터페이스가 있고 Verna 라는 클래스가 저 Car 인터페이스를 구현한다고 하자

Car.java
Verna.java

위에서 설명했듯이 Car 인터페이스를 구현하는 Verna 클래스에서는 인터페이스로 부터 받은 추상메소드에 대한 행동을 구현해야 한다

 

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

이제 구현체를 사용하는 방법을 알아본다.

 

위에 인터페이스 정의에서 적어놨듯이 인터페이스는 자체적으로 객체화를 할 수 없다.

 

그러면 어떻게 사용할 수 있을까??

 

간단하게는 다른 클래스 객체를 인스턴스화 하는 것과 같이 사용할 수 있다

Verna verna = new Verna();
verna.printSpeed();

하지만 우리는 대부분의 경우 하나의 인터페이스를 가지고 여러 개의 클래스를 구현할 것이다

 

인터페이스를 이용해서 구현한 클래스의 경우 그 클래스도 인터페이스의 타입에 속한다 (정의된 인터페이스는 타입으로 정의된 것과 마찬가지이니까)

 

DL

이 때, 우리는 Car 라는 변수에 Verna, Avante, Sonata 타입의 객체들을 담아서 사용할 수 있다

 

즉, 하나의 인터페이스 타입으로 그에 맞게 구현한 클래스들을 이용해 행동을 바꿀 수 있다 (다형성)

■ 인터페이스 상속

클래스가 슈퍼 클래스를 상속 받는 것과 마찬가지로, 인터페이스는 슈퍼 인터페이스를 상속 받아 확장할 수 있다

인터페이스 상속의 의미는 슈퍼 인터페이스에 정의 된 모든 메소드와 상속을 물려 받는다는 것이며, 확장하는 인터페이스에서는 새로운 상수와 메소드를 정의할 수 있다

하지만 클래스와는 다르게 인터페이스는 여러 개의 인터페이스를 상속 받을 수 있다 (다중상속이 가능!!)

 

문법은 다음과 같다

public interface Item1 {
    // apis
}

public interface Item2 {
    // apis
}

public interface Item extends Item1, Item2 {
    
}

 

만약에 어떤 클래스가 Item 클래스를 확장한다면, Item1 인터페이스, Item2 인터페이스 그리고 Item 에 정의된 모든 추상 메소드를 구현해야 함을 의미한다 (구현을 원치 않으면 abstract 로 정의할 수도 있다)

■ 인터페이스의 기본 메소드 (Default Method)

기본 메소드(Default Method) 란 Java8 부터 등장한 개념이며, 인터페이스 내에 구현이 된 메소드를 선언할 수 있음을 의미한다

즉, Java8 부터는 인터페이스에 추상 메소드 뿐만 아니라 구현이 된 메소드도 포함할 수 있음을 뜻한다

 

기본 메소드를 선언하는 방법은 간단하다

interface Item {
    // default 키워드를 사용하여 기본 메소드 정의
    default void printItem() {
         System.out.println("I'm printed by default method");
    }
}

 

?? 기본 메소드는 왜 생겨난걸까

Java의 설계자들은 항상 하위 호환성에 대해 고민해 왔다. 그로 인해서 Java 개발자 그룹에게 JDK 또는 JRE 버전이 업그레이드 되어도 현재 동작하고 있는 애플리케이션의 동작이 예기치 않게 깨지지 않을(멈추지 않음) 것이라는 강한 자신감을 심어준다

 

하지만 Java의 하위 호환성에 대한 강점을 달성하기 위해서는 플랫폼 상에 어떠한 제약이 필요했다

그 제약은 바로 이미 존재하는 인터페이스가 새로운 버전의 플랫폼에서 필수적으로 구현해야 하는 메소드가 추가되지 않아야 한다는 것이다

(왜냐하면, 인터페이스에 새로운 메소드가 추가 된다면, 구현하는 클래스에서는 필수적으로 새로 추가된 메소드에 대한 구현을 작성해야 하기 때문에)

 

이러한 문제를 해결하기 위해서는 인터페이스에 새로운 메소드의 선언을 추가한다 하더라도 하위 호환성이 깨지지 않으면서 인터페이스를 지속적으로 진화시킬 수 있는 방법이 필요했다

 

하위 호환성이 깨지는 거 없이 인터페이스에 새로운 메소드를 추가하는 것은 오래된 인터페이스 구현체에 대해 깨지지 않고 지속적으로 일하게 해줄 수 있는 무언가를 제공하는 것이 필요하게 되었는데, 이 개념이 바로 기본 메소드이다

♣ default 메소드의 기본 행동

  • 구현하는 클래스 내에서 인터페이스에 정의된 default 메소드를 override 하여 구현할 수 있다
  • 구현하는 클래스 내에서 default 메소드를 구현했다면 이 메소드가 실행된다
  • 만약 구현하는 클래스 내에서 default 메소드에 대한 구현체를 발견하지 못하면 인터페이스에 있는 default 메소드가 실행된다

♣ colliding default method

만약에 A, B 인터페이스에서 이름도 같고 signature 가 같은 default 메소드를 구현했다고 하고, C 라는 클래스가 A, B 인터페이스를 구현한다고 하자

 

이 때 구현하는 클래스 C 에서 printSomething() 메소드를 호출하면 Java 는 A, B 의 기본 메소드 중에서 

어떠한 메소드를 호출할지 모르는데, 이러한 현상을 colliding default method 라고 한다

 

colliding default method 일 때 나타나는 에러 문구

※ 이러한 현상을 해결하기 위해서는 구현하는 클래스에서 충돌하는 기본 메소드를 override 해서 행동을 정의해줘야 한다

만약에 override 한 메소드에서 인터페이스에 있는 기본메소드를 사용하기 위해서 다음과 같이 해주면 된다

 

InterfaceName.super.methodName() 을 통해서 슈퍼 인터페이스에 정의된 기본 메소드를 호출할 수 있다

 

※ default 메소드 내부에서 행할 수 있는 행동들

  • 인터페이스의 공용 API 에 있는 다른 메소드를 호출해라; 단 이런 메소드는 구현이 되어야 사용할 수 있다
  • 인터페이스 상에 private 메소드를 호출할 수 있다
  • 같은 인터페이스 또는 다른 어디에선가 정의 되어 있는 static 메소드를 호출할 수 있다
  • this 참조를 argument 를 통해 호출할 수 있다

 

■ 인터페이스의 static 메소드

Java8 버전 이상부터는 인터페이스 내에 static 메소드를 정의 및 구현할 수 있다

default 메소드와 유사하지만 default 메소드는 구현 클래스에서 재정의 할 수 있는 반면, static 메소드는 구현 클래스에서 구현할 수 없다

이는 이미 존재하는 구현 클래스에 추가/변경 없이 인터페이스에 안전하게 메소드를 추가할 수 있다는 이점이 있다

인터페이스 내에 정의된 static method

사용방법은 클래스의 static 메소드를 사용하는 것과 똑같이 하면 된다

■ 인터페이스의 private 메소드

Java9 부터는 인터페이스 내에 private 메소드를 선언 및 구현할 수 있다

private 메소드를 이용해서 인터페이스의 캡슐화가 가능해졌으며, private static 메소드도 추가할 수 있다

 

♣ private 메소드에 대한 규칙

  • private 메소드는 abstract 로 선언될 수 없다. 즉, 해당 인터페이스 내에서 구현해야 한다
  • private 메소드는 해당 인터페이스 내에서만 사용할 수 있다
  • private static 메소드는 static 또는 static 이 아닌 메소드 모두에서 사용할 수 있다
  • private 이면서 static 이 아닌 메소드는 private static 메소드 내부에서 사용할 수 없다

 

[참고사항]

Java in a Nutshell (www.oreilly.com/library/view/java-in-a/9781492037248/)

https://beginnersbook.com/2017/10/java-8-interface-changes-default-method-and-static-method/ 

https://www.baeldung.com/java-static-default-methods

https://howtodoinjava.com/java9/java9-private-interface-methods/ 

https://tutorialspoint.com/java/java_interfaces.htm

'공부 > whiteship-java' 카테고리의 다른 글

10주차: 멀티쓰레드 프로그래밍  (0) 2021.01.22
9주차: 예외 처리  (0) 2021.01.16
7주차: 패키지  (0) 2021.01.01
6주차: 상속  (0) 2020.12.24
5주차: 클래스  (0) 2020.12.19