📌 추상클래스와 인터페이스 개념
먼저 추상클래스와 인터페이스에 대해 개념을 학습한 뒤 어떤 상황에서 추상클래스와 인터페이스를 써야 하는지에 대해서 설명드리겠습니다.
추상 클래스 | 인터페이스 | |
공통점 | 1. 추상클래스(인터페이스도 일종의 추상클래스) 2. 하위 클래스에서 모든 추상메서드를 구현 해야 한다. 3. 그 자체가 객체로 인스턴스화 될 수 없다. (new 생성자 사용 X) -> extends 또는 implements 한 자식 클래스만 객체로 생성할 수 있다. |
|
차이점 | 1. 추상메서드 외 일반 멤버 변수와 메서드를 가질 수 있다. 2. extends 키워드를 사용 3. 단일 상속만 가능 4. 작업의 레벨 분할을 위해서 사용한다. |
1. 추상메서드와 상수만 사용 2. 인터페이스는 모든 메서드가 추상 메서드 3. implements 키워드 사용 4. 다중 상속 가능 5. 공동 작업에 용이하다. |
📚 인터페이스 정리
- 내부의 모든 메서드는 public abstract로 정의합니다. (default 메서드 제외)
- 내부의 모든 필드는 public static final 상수
- 클래스에 다중 구현 지원이 가능합니다.
- 인터페이스는 상속에 얽매이지 않고, 공통 기능이 필요할 때마다 추상 메서드를 정의해 놓고 구현하는 방식으로 추상 클래스보다 자유롭게 사용 가능합니다.
- 보통 ~~ able 형태로 네이밍 규칙을 따릅니다.
📚 추상클래스 정리
- 추상클래스는 하위 클래스들의 공통점들을 모아 추상화하여 만든 클래스입니다.
- 추상 클래스는 단일 상속만 허용합니다.
- 추상클래스는 추상 메서드 외에 일반클래스와 같이 일반적인 필드, 메서드, 생성자를 가질 수 있습니다.
- 인터페이스와 다른 점은, 추상클래스는 클래스 간의 연관 관계를 구축하는 것에 초점을 둡니다.
🔎 추상클래스 vs 인터페이스
추상클래스와 인터페이스의 공통점은 둘 다 추상메서드를 사용할 수 있다는 점입니다. 그럼 왜 굳이 2가지로 나누어서 사용할까요? 추상클래스와 인터페이스의 기능들을 살펴보면 추상클래스가 인터페이스의 역할을 다 하는데 왜 굳이 인터페이스가 필요할까요? 이론적인 차이점을 제외하고 두 개로 나누어서 사용하는 가장 큰 이유는 사용 용도 때문입니다.
사용의도 차이점
추상 클래스는 ~이다 (객체에 초점), 인터페이스는 ~을 할 수 있는 (기능에 초점)으로 구분되어 있습니다.
자바의 특성상 한 개의 클래스만 상속이 가능하여 해당 클래스의 구분을 추상클래스 상속을 통해 해결하고, 할 수 있는 기능들을 인터페이스로 구현합니다. 이렇게 말로 설명드리면 어려울 수 있으니 예제를 통해 더 자세히 설명드리겠습니다.
Creature 추상클래스
public abstract class Creature {
private int age;
private int x;
private int y;
public Creature(int age, int x, int y){
this.age = age;
this.x = x;
this.y = y;
}
//나이를 먹는 기능
public void age() {
age++;
}
//x축으로 이동하는 기능
public void move(int xDistance) {
x += xDistance;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
//추상 메서드
public abstract void attack();
public abstract void printInfo();
@Override
public String toString() {
return "x : " + x + ", y : " + y + ", age : " + age;
}
}
기본적으로 생명체가 갖고 있는 요소인 나이와 , x축과 y축으로 이동할 수 있게 추상클래스를 만들어두었고, 생명체라면 일반적으로 나이를 먹는 기능과, x축으로 이동하는 것은 다 가능하기 때문에 추상메서드가 아닌 일반 메서드로 구현하였습니다.
추상 메서드로는 공격하는 기능과 정보를 출력하는 기능을 만들었습니다. 모든 생명체에 필요한 기능이지만 각각 생명체에 따라 다른 기능으로 구현을 해야 하기 때문에 위 두 메서드는 추상메서드로 선언하여 하위클래스에서 처리하도록 하였습니다.
Talkable 인터페이스
public interface Talkable {
//1.
//abstract void talk();
//2.
void talk();
//1번과 2번은 같습니다.
}
Talkable 인터페이스는 추상메서드 하나만 생성했습니다. 또한 인터페이스 메서드를 선언할 때는 abstract키워드를 붙이지 않아도 됩니다(인터페이스 내의 메서드는 기본적으로 abstract). 인터페이스의 추상메서드는 abstract를 빼도 보통 interface의 명명규칙은 ~able(~할 수 있는)로 짓는 경우가 많습니다. ex) Comparable, Parsable
Human 추상클래스
public abstract class Human extends Creature implements Talkable{
public Human(int age, int x, int y) {
super(age,x,y);
}
@Override
public void attack(){
System.out.println("도구를 사용!");
}
@Override
public void talk(){
System.out.println("사람은 말을 할 수 있음!");
}
}
인간 클래스에 attack 추상메서드는 구현해 주고, printInfo 메서드는 따로 구현하지 않았습니다. 왜 printInfo는 추상메서드인데 구현하지 않았을까요?? 그 이유는 사람 클래스도 추상 클래스이기 때문에 하위 클래스에서 구현해 준다면 위처럼 사용이 가능합니다. 또한 인터페이스의 추상메서드를 구현하기 위해 talk 메서드를 구현하였습니다.(구현하지 않으면 컴파일에러)
Animal 추상클래스
public abstract class Animal extends Creature{
public Animal(int age, int x, int y) {
super(age,x,y);
}
@Override
public void attack(){
System.out.println("몸통 박치기");
}
}
동물 클래스도 마찬가지로 attack메서드를 오버라이딩 해주었습니다.
Swimable 인터페이스
public interface Swimable {
void swimDown(int yDistance);
}
Swimable 인터페이스에서 swimDown 추상 메서드를 만들었습니다. 저는 Harry와 Dolphin이라는 클래스를 만들 것입니다. Harry는 사람이고 Dolphin은 동물입니다. 그러나 두 생명체 모두 수영을 할 수 있습니다. 이럴 경우 생명체 클래스에 swimDown 추상메서드를 만들어야 하나 생각할 수 있습니다. 하지만 사람 중에는 수영을 못하는 사람도 있을 것이고, 동물 중에도 수영을 못하는 동물이 있을 것입니다. 따라서 이럴 경우 인터페이스로 선언하여 수영을 할 수 있는 클래스에 만들어 주면 가독성과 유지보수 측면에서도 뛰어나게 됩니다.
Harry 일반클래스
public class Harry extends Human implements Swimable{
public Harry(int age, int x, int y){
super(age, x, y);
}
@Override
public void swimDown(int yDistance){
setY(getY() - yDistance);
if(getY() < -10){
System.out.println("너무 깊이 들어가면 죽어!");
}
}
@Override
public void printInfo(){
System.out.println("Harry -> " + toString());
}
}
Harry 클래스에서는 수영을 하며 y축으로 -10 이하로 내려가면 경고를 알리기 위해 재정의 하였습니다.
Dolphin 일반클래스
public class Dolphin extends Animal implements Swimable{
public Dolphin(int age, int x, int y){
super(age, x, y);
}
@Override
public void swimDown(int yDistance){
setY(getY() - yDistance);
}
@Override
public void printInfo(){
System.out.println("Dolphin -> " +toString());
}
}
Dolphin 클래스에서는 수영을 하며 아래로 내려갈 수 있는 기능을 재정의 하였습니다. 이처럼 상속관계에서 swimDown기능을 재정의 하였다면(Creature에서 추상메서드로 swimDown 메서드 생성) Lion 일반 클래스에서도 재정의 해줘야 하고 이것은 불필요한 메서드까지 구현해야 하는 의무가 발생하게 됩니다. 따라서 상속관계가 아닌 클래스 간의 연관관계로 서로 관련성이 없는 클래스를 묶어주고 싶을 경우 인터페이스를 통해 구현하는 것이 맞는 방법입니다.
Lion 일반클래스
public class Lion extends Animal{
public Lion(int age, int x, int y) {
super(age, x, y);
}
@Override
public void printInfo(){
System.out.println("Lion -> " + toString());
}
}
Lion 일반 클래스는 최상단의 printInfo를 추상 메서드로 구현하였기 때문에 오버라이딩을 해주었습니다.
📚 총정리
추상클래스는 클래스 간의 상속관계를 따져서 조상클래스의 기능이 하위 클래스에 똑같이 필요한 경우 사용합니다.
(ex. attack, printInfo)
인터페이스는 상속관계가 아닌 클래스 간의 연관관계를 따져서 서로 관련성이 없는 클래스를 묶어주고 싶을 경우 사용합니다.
(ex. Talkable, Swimable)
❓ 왜 클래스 간 다중 상속이 불가능할까
만약 자바에서 다중 상속을 허용한다면 다이아몬드 문제가 가능할 것입니다.
실제로 Father와 Mother 클래스에서 speak();라는 메서드를 구현하였고, Child 클래스에서 speak(); 메서드를 사용하였다면 Father클래스에서 구현한 speak(); 메서드인지 Mother클래스에서 구현한 speak(); 메서드인지 알 수 없습니다. 그렇기 때문에 자바 개발자는 multiple inheritance가 필요한 경우 인터페이스의 다중 상속을 이용해야 합니다.
❓ 왜 인터페이스는 다중 상속이 가능할까
그건 바로 인터페이스는 실질적인 구현을 하지 않고, 실질적인 구현은 상속받은 클래스에서만 가능하기 때문입니다.
'CS > Java' 카테고리의 다른 글
[Java] Error와 Exception (0) | 2024.05.02 |
---|---|
[Java] static 이란 ? (0) | 2024.04.29 |
[Java] 오버로딩(Overloading)과 오버라이딩(Overriding) 차이 (0) | 2024.04.14 |
[Java] 제네릭이란?(Generic) (0) | 2024.04.04 |
[Java] Garbage Collection(GC) 이란? (0) | 2024.03.01 |