6) 객체지향 프로그래밍8 - 인터페이스

dev-mage·2022년 10월 23일
0

Hello Java World!

목록 보기
18/32
post-thumbnail

Java의 인터페이스

인터페이스(interface)

인터페이스는 일종의 추상 클래스이다. 인터페이스도 추상 클래스처럼 추상 메서드를 갖지만 추상화 정도가 보다 높아 추상 클래스와 달리 일반 메서드 또는 멤버 변수를 구성원으로 가질 수 없고 오직 추상 메서드와 상수만을 가질 수 있다. 추상 클래스를 부분적으로만 완성된 ‘미완성 설계도’라고 한다면, 인터페이스는 밑그림만 그려져 있는 ‘기본 설계도’라 할 수 있다. 인터페이스는 키워드로 class 대신 interface를 사용하며 접근 제어자로 클래스처럼 public 또는 default만 허용한다.

interface 인터페이스명 {
		public static final 타입 상수명 =;
		public abstract 메서드명(매개변수...);
}

인터페이스에는 다음과 같은 제약사항이 있다.

  • 모든 멤버 변수는 public static final이어야 하며 이는 생략 가능.
  • 모든 메서드는 public abstract이어야 하며 이는 생략 가능(static 메서드와 default 메서드 제외).

해당 제약사항은 인터페이스에 정의된 모든 멤버에 예외 없이 적용 되는 사항이기 때문에 제어자를 생략할 수 있는 것이며, 생략된 제어자는 컴파일 시에 컴파일러가 자동적으로 추가해준다.

interface Implementable {
		int YEAR = 2022; // public static final int YEAR = 2022;

		void helloWorld(); // public abstract void helloWorld();
}

인터페이스의 상속

인터페이스는 인터페이스로부터만 상속 받을 수 있으며, 클래스와 달리 다중 상속이 가능하다. 인터페이스는 클래스와 달리 Object 클래스와 같은 부모가 없다.

interface Implementable1 extends Implementable2, Implementable3 { ... }

클래스의 다중 상속을 불허한 이유는 다이아몬드 문제 처럼 부모 멤버의 출처가 모호해지는 것을 막기 위해서라고 했다. 그렇다면 인터페이스의 다중 상속은 어떻게 가능한 것일까?

예를 들어 다중 상속이 가능한 클래스가 있다고 하자. 이 클래스를 인스턴스화하면 해당 인스턴스는 모든 부모 클래스의 멤버를 상속 받을 것이다. 하지만 만약 서로 다른 부모 클래스의 메서드나 생성자에서 같은 멤버 변수를 초기화하는 경우에는 어떻게 해야할까? 어느 것을 우선으로 해야할까? 인터페이스에는 상수를 제외한 필드는 가질 수 없으므로 이런 문제가 발생하지 않는다. 메서드 역시 구현부가 없기 때문에 다중 상속을 해도 문제가 없는 것이다.

인터페이스의 구현

인터페이스도 추상 클래스처럼 그 자체로는 인스턴스를 생성할 수 없으며, 추상 클래스가 상속을 통해 추상 메서드를 완성하는 것처럼, 인터페이스도 자신에게 정의된 추상 메서드의 바디를 작성할 클래스가 있어야 한다. 추상 클래스를 상속 받는 클래스가 extends를 사용해 확장한다면 인터페이스는 구현한다는 의미로 키워드 implements를 사용한다.

class 클래스명 implements 인터페이스명 { ... }

인터페이스를 구현하는 클래스는 인터페이스에 정의된 모든 추상 메서드를 구현해야 하며 일부만 하는 경우 abstract를 붙여 추상 클래스로 선언해야 한다. 또한 클래스 상속과 인터페이스 구현을 동시에 할 수도 있다.

인터페이스와 다형성

인터페이스는 인스턴스의 참조 변수, 메서드의 매개변수 및 리턴 타입으로 사용될 수 있다. 인스턴스가 인터페이스를 참조하는 경우는 해당 인터페이스를 구현한 클래스의 인스턴스만 가능하다. 메서드의 매개변수 및 리턴 타입으로 지정된 경우도 해당 인터페이스를 구현한 클래스의 인스턴스를 제공해야 한다.

interface Implementable { ... }
class Impl implements Implementable { ... }

// 인스턴스의 참조 변수
Implementable i = (Implementable) new Impl();
 또는
Implementable i = new Impl();

// 메서드의 매개변수
void method(Implementable i) { ... }

// 메서드의 반환 타입
Implementable method() { 
	... 
	return new Impl();
}

인터페이스의 장점

인터페이스의 장점은 다음과 같이 정리해 볼 수 있다.

  • 개발 시간 단축
    • 인터페이스를 작성하면 메서드를 호출하는 쪽에서 메서드 내용에 관계 없이 메서드 선언부만 알면 되기 때문에 이를 사용해 프로그램을 작성할 수 있다. 인터페이스를 구현하는 클래스가 다 작성될 때까지 기다리지 않고도 개발을 진행할 수 있는 것이다.
  • 개발 표준화
    • 프로젝트에 사용되는 기본 틀을 인터페이스로 작성한 다음. 개발자들에게 인터페이스를 구현하여 프로그램을 작성하도록 함으로써 보다 일관되고 정형화된 프로그램 개발이 가능하다.
  • 독립적인 프로그래밍
    • 인터페이스를 이용하면 클래스의 선언과 구현을 분리시킬 수 있기 때문에 실제 구현에 독립적인 프로그램을 작성하는 것이 가능하다.

default 메서드와 static 메서드

원래 인터페이스에는 추상 메서드만 선언할 수 있었으나 JDK 8부터 default 메서드와 static 메서드도 추가할 수 있게 되었다. 인터페이스는 표준화된 프로그래밍을 가능하게 만들지만 추가 사항이 생길 경우 고려해야 할 점이 많다. 인터페이스를 구현한 모든 클래스들이 새로 추가된 내용을 구현해야 하기 때문이다. 이처럼 인터페이스에 변경이 생겼을 때 호환성을 제공하기 위해 default 메서드가 등장하게 되었다. default 메서드는 인터페이스에 기본적인 구현을 제공하는 메서드로 해당 인터페이스를 구현한 클래스에 자동으로 추가되므로 클래스를 변경하지 않아도 된다.

default 메서드는 메서드를 선언할 때 키워드 default를 붙이며 메서드 바디({ })를 가져야 한다. 접근 제어자는 public만 가능하며 생략할 수 있다.

interface Implementable {
		void method(); // 추상 메서드
		default void defaultMethod();
}

대신, 새로 추가된 디폴트 메서드가 기존의 메서드와 이름이 중복되어 충돌하는 경우가 발생할 수 있다. 이런 경우 다음과 같은 규칙을 따른다.

  • 동일한 default 메서드가 있는 서로 다른 인터페이스를 구현하는 클래스의 경우 위에서 언급했듯이 한 클래스에서 여러 인터페이스를 구현할 수 있다. 여러 인터페이스에서 똑같은 이름의 메서드가 있어도 충돌이 생기지 않는 이유는 메서드 바디가 없고 인터페이스를 구현하는 클래스에서 이를 오버라이딩하기 때문이다. 그런데 default 메서드는 내용이 있는 메서드이다. 이런 경우 컴파일 에러가 발생하며 클래스에서 default 메서드를 오버라이딩해야 한다.
  • default 메서드와 부모 클래스의 메서드가 동일한 경우 이 경우 부모 클래스의 메서드가 상속되고 default 메서드는 무시된다.

References

0개의 댓글