공부방/JAVA

자바 인터페이스 - 백기선 자바라이브스터디

EVO. 2023. 9. 7. 00:50

인터페이스 정의하는 방법

인터페이스란 객체와 객체 사이에서 일어나는 상호 작용의 매개로 쓰인다. 누군가는 이를 다리 역할로 볼 수 있다고 하며 실제 구현 클래스의 설계도라고 부르기도 한다. 

 

인터페이스의 선언은 예약어로 class가 아닌 interface 키워드를 사용하며, 메소드의 접근 제어자로는 자바8 기준으로는 public, default, static이 가능하며 자바9부터는 private도 가능해졌다

 

 

각각은 뒤에서 자세히 살펴본다

  1. 상수 : 인터페이스에서 제공한 값을 바꾸지 말고 그대로 사용
  2. 추상메소드 : 선언만 하고 실제 구현 클래스에서는 무조건 오버라이딩 하여 구현
  3. 디폴트메소드 : 인터페이스에서 직접 구현, 구현 클래스는 해당 메소드를 오버라이딩 하지 않아도 된다
  4. 스태틱메소드 : 인터페이스의 스태틱 메소드 

인터페이스는 다음과 같이 생략을 해도 자동으로 컴파일러가 추가해준다

변수는 반환타입 + 상수명 = 값; 만 선언해도 public static final이 무조건 붙여진다.

메서드는 반환타입 + 메소드명(매개변수...)만 선언해도 public abstract가 컴파일러에 의해 붙여진다.

 

인터페이스 구현 방법 

클래스 선언부에 implements 키워드를 추가하고 상속받을 인터페이스명을 적으면 해당 클래스는 상속받은 인터페이스의 구현 클래스라고 부른다.

public class 구현 클래스 명 implements 인터페이스 명 {
	// 인터페이스에 선언된 추상 메소드의 실체 메소드 선언
}

상속받은 인터페이스에서 선언했던 추상 메소드를 무조건 오버라이딩하여 구현 클래스에서 구현해야 한다. 

 

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

인터페이스를 상속받은 클래스는 인터페이스 레퍼런스로 클래스를 가리킬 수 있다. 그리고 이 래퍼런스는 해당 인터페이스에서 선언한 메서드만 호출 할 수 있다. 이는 너무나 당연해서 기본적인건 더이상 설명하지 않고 인터페이스가 어떻게 객체지향적인 코드를 만들 수 있는 지에 대해 설명하겠다

 

다음부터 나오는 코드는 https://school.programmers.co.kr/app/courses/17778/dashboard 에서 설명해주신 코드를 쓴다는 점을 알리고 여기서 코드를 확인할 수 있다.

https://github.com/minsang-alt/java/tree/main/src/main/java/com/example/javaspring/studyInterface/section0

 

먼저 인터페이스를 사용하지 않은 예제이다

 

 

이러한 상황에서 개발 도중 새로운 메세지구현 클래스로 교체하라는 요구사항이 들어온다고 가정하자

RealMessageSender로 바꾸기 위해 클라이언트의 코드에서 다음과 같이 수정하면 된다

이번엔 인터페이스를 사용한 예제이다

 

인터페이스를 사용하지 않았을 때와 사용했을때를 비교해보면 다음과 같은 특징이 있다

  • Client가 더이상 구현체에 의존하지 않고 인터페이스에 의존한다
  • Client는 더이상 어떤 구현체를 쓸 지 몰라도 된다. 즉, 구현체를 Fake=>Real로 바꾸는 요구사항이 와도 Client의 코드는 수정되지 않는다
  • MessageSender는 인터페이스이고 다형성을 제공한다. 즉, 인터페이스의 레퍼런스를 통해 구현체를 사용하고 있음을 알 수가 있다 

앞으로 코드를 작성할 때 인터페이스를 참조하도록 해서 최대한 구현체와 구현체를 사용하는 클래스를 떨어뜨리는 것이 앞으로 유지보수를 하는 데 굉장한 효과를 볼 수가 있다. 이걸 OCP 개방-패쇄 원칙과 DIP 의존관계 역전 원칙이라고도 부르는데 더 자세한 내용은 김영한님의 스프링 기본편에서 배울 수 있다.

 

인터페이스의 상속

인터페이스는 추상클래스와 달리 다중 상속이 가능하다

단지 implements 키워드 하나만 붙이고 ,(콤마)로 분리하여 여러개를 상속받으면 된다 

 

추상클래스에서는 여러개를 상속받으면 만약 같은 메소드시그니처가 있다면 누굴 사용해야할지 컴파일러가 판단을 할 수가 없어서 엄격히 금지가 됐지만 인터페이스에서는 다중상속을 허용함으로써 주의할 점이 생겼다 

 

1. 인터페이스가 여러개의 인터페이스를 상속받을 때 생기는 문제점

 

상위 인터페이스에 있는 반환값만 다른 메소드 시그니처가 같은게 존재한다면 이를 상속받는 인터페이스는 컴파일에러가 발생한다 

 

2. 메소드 시그니처와 반환 타입이 같은 경우 

다음은 자바8에서 생긴 default에서 생기는 문제이다 먼저 아래의 default개념을 파악하고 보는 것이 좋을 것 같다

이때는 해결책이 있다 메소드를 재정의하면 된다 C인터페이스에 default void m1(){}으로 재정의를 하면 된다 

 

 

인터페이스의 기본 메소드 (default Method) - java 8부터 제공

  • 인터페이스에 메소드 선언이 아니고 구현체를 제공
  • 해당 인터페이스를 구현한 클래스의 어떠한 영향 없이 새로운 기능을 추가하는 방법
  • Object 클래스가 제공하는 equals나 hashCode와 같은 기본 메소드는 사용 불가능
  • 인터페이스를 상속받은 인터페이스에서 다시 추상 메소드로 변경 가능
  • 인터페이스 구현체가 default method를 재정의 할 수 있다

default Method는 오픈소스 라이브러리에서 큰 힘을 발휘한다. 예를 들기 위해 자바9 레퍼런스를 보면

( https://docs.oracle.com/javase%2F9%2Fdocs%2Fapi%2F%2F/java/util/Collection.html )

Collection 인터페이스에 4개의 default method가 구현됨을 알 수가 있다. default method를 구현한 이유는 개발한 사람이 더 잘알겠지만 그중 추측 할 수 있는 것은 다음과 같은 이유이다.

  • 전세계 사람들이 해당 인터페이스를 이용해 사용중이다. 이때 개발자는 새로운 메소드를 추가 하고 싶은데 단순히 추상메소드를 만들게 되면 이 인터페이스를 오버라이딩한 전세계의 개발코드는 오류가 발생하고 수정하는 일이 발생 할 수 있다
  • 위의 이유 때문에 개발이 조심스러워졌는데 자바8부터 default 메소드를 만들면서 추가적으로 구현해야 할 혹은 필수적으로 존재해야 할 메소드를 넣을 수 있게 되었다 
  • 이렇게 인터페이스를 보완할 방법이 생긴 것이다. 

별거 아니지만 직접 구현해 사용해보자 

https://github.com/minsang-alt/java/tree/main/src/main/java/com/example/javaspring/studyInterface/section2

 

주의할 점

  • default method로 제공한 기능이 항상 제대로 동작할 것이라는 보장이 없다
  • default method를 구현할 때 다른 개발자들이 어떻게 사용할 지 고민을 해야한다. 만약 그런 사고과정없이 사용한다면
    개발자는 모르고 사용하다가 다음과 같은 NullPointerException이 발생할 수 있다

만약 저걸 상속받은 인터페이스가 default method를 제공하고 싶지 않는다면 추상메소드로 다시 정의할 수도 있다 

void dontWriteThisMethod(); <= 이렇게

 

주의할점2

두개의 인터페이스에서 동일한 default method를 구현했다고 가정하자 그리고 이 두 인터페이스를 상속받고 있는 클래스는 다음과 같은 문제에 직면 할 수가 있다.

 

이런 케이스에서는 구현 클래스에서 직접 오버라이딩하여 사용해야 컴파일 에러가 발생하지 않는다.

 

인터페이스의 default method가 없던 시절

인터페이스의 여러가지 메소드들 중 일부분만 쓰고 싶었을 때 자바 8 이전에는 다음 행위를 거쳤다. 

중간에 추상 클래스를 만들어서 이 추상 클래스를 확장하는 구현체에서는 필요한 메소드만 구현할 수 있도록 하였다 

자바 8 이후 인터페이스에서 default method가 제공됨에 따라 위와 같은 추상클래스를 추가하지 않아도 된다.

이로써 추상클래스를 만들지 않아도 상속에서 자유로워지게 되었다 

인터페이스의 static 메소드 (자바 8)

static {리턴타입} {메소드명}({파라미터...}){
	// do something
}

자바 8이후 스태틱 메소드를 구현할 수 있게 되었다. 인터페이스에 위와 같이 선언함으로 기존에 쓰던 static메소드와 같음으로 넘어가겠다

 

추상 클래스는 이제 쓸모 없어졌을까

일단 당연한 점은 자바8이 생기기 전에는 추상클래스를 이용해 만들었던 라이브러리들이 많이 있음으로 필요가 없어지는 것은 아니다. 

그리고 인터페이스에서는 변수선언이 불가능하다

변수선언이 불가능하고 상수로만 선언할 수밖에 없다. 즉, 변경 불가능한 값만이 가능한 것이다.

인터페이스 존재하는 변수는 무조건 "public static final"로 선언되며 이를 생략할 수 있다. 

 

추상클래스에서의 장점들이 인터페이스로 옮겨져서 점점 추상을 선언할 이유가 없어지고 있지만, 여전히 추상클래스에서만 되는 변수선언과 같은 것이 있기 때문에 계속 관심을 두어야 할 것 같다. 

 

인터페이스의 private 메소드, 자바 9

자바9부터는 private method, private static method가 추가되었다. 

단지 특정 기능을 처리하기 위한 내부 메소드인데 public으로 선언해야한다는 문제를 해결하기 위함이다.

 

private method 규칙

  • private 메소드는 구현부를 가져야 한다
  • private static 메소드는 기존 static메소드 특징과 똑같다 

private 메소드가 추가됨으로서 코드의 중복을 피하고 interface에 대한 캡슐화를 지킬 수 있게 된다.

 

출처

가장 많이 참고한 https://five-cosmos-fb9.notion.site/4b0cf3f6ff7549adb2951e27519fc0e6

 

인터페이스

WhiteShip Java Study 시즌 1

five-cosmos-fb9.notion.site

 

https://school.programmers.co.kr/app/courses/17778