해결된 질문
작성
·
36
·
수정됨
2
안녕하세요 ㅎㅎ
좋은 강의 제공해주셔서 감사합니다.
추상 클래스와 인터페이스 사용 시점에 대해 궁금한 것이 있습니다.
제가 처음에 생각한 내용은
추상 클래스 : 부모 클래스와 자식 클래스가 공통으로 제공하는 내용(메서드)도 존재하지만, 자식클래스 별로 다른 내용을 제공할 때는 abstract 메서드를 사용하여 구현을 강제시킨다.
인터페이스 : 부모와 자식 간에 공통으로 가지는 내용(메서드 내용이 동일한)이 없으므로, 상속받는 클래스가 반드시 메서드를 구현한다.
그런데 JAVA8부터 인터페이스에 default 메서드가 추가되었더군요.
인터페이스에 default 메서드를 선언하면, 인터페이스를 상속받는 클래스에서 구현을 하지 않고도 사용할 수 있더라구요.
interface Shape {
final int SIZE = 5;
void render();
default int getSize() {
return SIZE;
}
}
class Rectangle implements Shape {
@Override
public void render() {
StringBuilder buffer = new StringBuilder();
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
buffer.append("*\t");
}
buffer.append("\n");
}
System.out.println(buffer);
}
}
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int input = sc.nextInt();
Shape shape;
if (input == 0) {
shape = new Rectangle();
} else {
shape = new Triangle();
}
shape.render();
System.out.println(shape.getSize());
}
}
그렇다면 default 메서드가 추상 클래스의 일반 메서드(추상 클래스가 아닌)와 동일하게 작동하게 되는데
public abstract class Shape {
public static final int SIZE = 5;
public abstract void render();
public int getSize() {
return SIZE;
}
}
추상 클래스와 인터페이스를 사용하는 시기(?)를 어떻게 구별해야할까요? 구글링을 해보니 인터페이스에 default 메서드가 추가된 이유가 추후에 인터페이스에 새로운 메서드를 추가했을 때, 해당 인터페이스를 상속하고 있는 클래스에서 오류가 발생하지 않기 위해서라고 하는데
그 오픈소스가 엄청 유명해져서 전 세계 사람들이 다 사용하고 있는데, 인터페이스에 새로운 메소드를 만들어야 하는 상황이 발생했다. 자칫 잘못하면 내가 만든 오픈소스를 사용한 사람들은 전부 오류가 발생하고 수정을 해야 하는 일이 발생할 수도 있다. 이럴 때 사용하는 것이 바로 default 메소드다.
결론적으로 내용을 정리하게 되면
추상 클래스에서 새로운 메서드를 추가해야 한다면 abstract를 추가하지 않는다.
인터페이스에서 새로운 메서드를 추가해야 한다면 default 메서드를 사용한다.
default 메서드가 추가 되면서 추상 클래스와 인터페이스를 언제 사용해야 하는지 잘 모르겠습니다...!
감사합니다!
답변 1
2
답변에 앞서 매우 좋은 질문이라는 점을 꼭 말씀드리고 싶습니다. 보강을 만든다면 주제로 삼는 것이 좋겠다는 판단이 들 정도입니다! 정말 진지하게 생각해보겠습니다. 😄
Java에서 interface의 역할은 '선언' 입니다. 다만, 정의를 문법적으로 강제화 한다는 점에서 C/C++의 헤더 파일과 근본적인 차이가 있습니다. 이름 그대로 API의 명세를 명확히 할 수 있고 모든 파생 형식에서 해당 API의 구현체가 존재한다는 점을 확신할 수 있습니다. 물론 결과적 관점에서 생각하면 추상 클래스와 차이가 없어진 것일 수 있습니다. 그러나 그렇다고 해서 인터페이스의 존재 이유가 근본적으로 달라진 것은 아닙니다. 여전히 '선언'을 위한 용도로 활용됩니다.
C++에는 헤더 파일이 이 역할을 하고 있기 때문에 같은 의미에서 존재 이유가 명확합니다. 다만 거기에 예외적인 '선언 및 정의'를 포함 시킨 것이 default라고 이해하는 것이 좋겠습니다. 그리고 그 이유도 수퍼 클래스 수준에서 오류를 해결하기 위함이라는 것이고요.
마치 타임머신을 이용하는 것이 비유 할 수 있겠습니다. 제가 부모 클래스를 '현재' 파생 클래스를 '미래'로 상정하고 설명한 경우가 많았는데 그 관점에서 생각해보면 default 메서드의 존재 이유를 더 쉽게 받아들일 수 있지 않을까 생각합니다. 파생 클래스 시점이 '현재'이고 대략 100개의 파생 클래스 중 20개 이상의 파생 형식에서 구조적인 오류가 발생한 상황이데 이를 각각 수정하기 보다 타임머신을 타고 과거로 돌아가 새로운 기능 하나를 넣어 쉽게 해결될 수 있다 생각해보시기 바랍니다. 그러나 언어의 문법이 이를 막고 있다면? 예외적인 허용으로 규칙을 변경한다면 마치 법을 개정해가며 사회가 유지되는 것처럼 대응 할 수 있겠습니다.
정리하자면...
인터페이스의 목적(설계 관점에서 선언만 존재)은 바뀌지 않았습니다. 거기에 새로운 문법을 넣어 문제를 해결할 수 있는 길이 하나 더 열린 것 뿐입니다. 단, 이를 남용하면 결과적으로 추상 클래스와 달라지지 않으며 인터페이스의 존재 가치를 훼손하게 될 것이고 구조적으로 아쉬운 결과를 초래하게 되겠습니다.
참고하시기 바랍니다. 😄