전통적으로 인터페이스는 메서드 내역을 정의하고 이를 클래스에서 구현해서 사용하는 기능이였다.
회사의 보안팀의 암호화 알고리즘 솔루션을 제공하는 예제 코드로 살펴본다.
public interface CompanyEncryption {
public byte[] encrypt(byte[] bytes) throws Exception;
public byte[] decrypt(byte[] bytes) throws Exception;
}
위의 코드에서는 인코딩을 위한 encrypt와 decrypt를 구현해서 사용해야하는 인터페이스 이다.
하지만, 기존 자바에서는 여기서 다음과 같이 새로운 기능을 추가하게 되면 문제가 발생한다.
public interface CompanyEncryption {
...
public boolean isEncoded(byte[] bytes) throw Exception;
}
위와 같이 문자가 암호화된 것인지 판단하고 싶어, isEncoded라는 메서드를 추가하였다.
이를 추가한 순간, 이 인터페이스를 사용하는 모든 클래스들에서 컴파일 에러가 날 것이다.
그 이유는, 인터페이스를 implements하고 있는 클래스들은 반드시 인터페이스가 가진 메서드들을 구현해야하기 때문이다.
이러한 이유 때문에 해당 기능을 다른 인터페이스로 분리하여 추가하는 방법을 사용할 수 있다.
public interface EncryptionChecker {
public boolean isEncoded(byte[] bytes) throw Exception;
}
이렇게 별도의 인터페이스로 분리하게 되면, 컴파일 문제는 해결할 수 있지만 기능이 추가될 때 마다 인터페이스를 새로만들고 이를 추가하는 매커니즘을 반복해야 한다.
기존 인터페이스에서는 다음과 같은 제약이 있었다.
- 상수를 선언할 수 있다. 상수는 반드시 값이 할당되어야 하며 final로 인식된다.
- 메서드는 반드시 추상 메서드여야 한다.
- 인터페이스를 구현한 클래스에서 메서드를 구현하지 않으려면 클래스는 추상클래스여야 한다.
- 인터페이스에 선언된 상수와 메서드는 public 키워드가 없어도 public으로 인식한다.
- 중첩 클래스 및 인터페이스를 선언할 수 있다.
자바 8 부터 이러한 인터페이스 제약에 다음 두가지 항목을 추가함으로서 앞서 말한 문제점을 해결하며 큰 변화를 가져왓다.
- 실제 코드가 완성되어 있는 static 메서드를 선언할 수 있다.(구현 클래스에서 오버라이딩 불가능)
- 실제 코드가 완성되어 있는 default 메서드를 선언할 수 있다.(구현 클래스에서 오버라이딩 가능)
- private 메서드를 추가할 수 있다.(구현 클래스에서 오버라이딩 할 수 없고 내부 공통 로직에서 사용할 수 있음)
그렇다면 이러한 인터페이스와 추상클래스의 차이점은 무엇인가?
- 추상클래스는 멤버 변수를 가질 수 있지만, 인터페이스는 static 변수만을 가질 수 있다.
- 클래스는 하나만 상속받을 수 있지만, 인터페이스는 여러 개를 구현할 수 있다.
하지만 이렇게 인터페이스를 클래스처럼 사용하게 되며 문제가 발생하게 된다.
원래 자바에서는 다중 상속을 지원하지 않지만, 인터페이스가 위와같이 변경되게 되며 다중 상속처럼 사용할 수 있게 되어 다이아몬드 상속 문제가 발생하게 된다.
아래 코드는 다이아몬드 상속 문제를 보여준다.
public interface Human {
...
public String getSex();
...
}
이 Human 인터페이스를 상속받는 Male, Female 인터페이스가 있다고 해보자.
public interface Male extends Human {
public static String SEX = "male";
@Override
default String getSex() {
return Male.SEX;
}
}
public interface Female extends Human {
public static String SEX = "female";
@Override
default String getSex() {
return Female.SEX;
}
}
그리고 이러한 인터페이스를 구현한 worker 클래스가 있다고 해본다.
public class Worker implements Female, Male {
}
이렇게 Female, Male 인터페이스를 implements 하는 순간 컴파일 에러가 나게 된다.
이유는 Male 과 Female 인터페이스 둘다 getSex 메서드를 default로 지정했기 때문이다.
그렇기 때문에 Worker 클래스에서는 getSex라는 메서드를 어떤 인터페이스에서 가져올지 모르게 되어 컴파일 에러가 나게 되는 것이다.

이를, 해결하기 위해 아래와 같이 Male Interface를 Class로 변경할 수 있다.

이렇게 Male을 클래스로 변경 후, getSex를 호출하면 Male의 성별이 나오는 것을 볼 수 있다.
이는, 클래스와 인터페이스 모두 상속, 구현 할 때 같은 메서드명이 있으면 상속받은 클래스의 메서드가 우선적으로 호출되기 때문이다.
위와 같이 자바에서도 인터페이스를 통해 다중상속이 가능하게 되었고, 다음과 같은 중요한 원칙을 가지게 된다.
- 클래스가 인터페이스에 대해 우선순위를 가진다.(동일한 메서드를 가질 때 클래스가 먼저 호출)
- 위의 조건을 제외하고 상속 관계에 있을 때, 하위 클래스/인터페이스가 상위 클래스/인터페이스보다 우선적으로 호출된다.
- 위의 조건을 제외하고 호출하고자하는 메서드를 명확하게 지정하지 않았을 때는 컴파일 에러가 발생한다.
결론적으로는, 여러 유용한 기능이 인터페이스에 추가되며 다중상속을 받을 수 있게 변했지만 그만큼 문제가 발생할 수 있는 부분도 커졌다는 것을 염두에 두어야 한다.
'공부 > Java' 카테고리의 다른 글
| [MapStruct] 03. 심화 사용법 (0) | 2025.04.07 |
|---|---|
| [MapStruct] 02. 기본 사용법 (0) | 2025.04.07 |
| [MapStruct] 01. MapStruct란 무엇인가? (0) | 2025.04.07 |