자바에서 다중 상속을 허용하지 않는 이유
- 자바에서 다중 상속을 하지 않는 이유는 모호성을 제거하기 위해서이다.
- 다중 상속의 장점은 한 클래스가 가질 수 있는 기능이 풍부해질 수 있다는 점이다.
- 반면 다중 상속을 하게 되면 최상위 클래스에서 정의한 메서드를 여러 상위 클래스에서 오버라이딩하게 되면 하위 클래스가 어떤 메서드를 사용해야 할 지 모호해 진다.
- 이러한 문제를 diamond problem 이라고 한다.
여러 개의 인터페이스 구현
- 클래스 간 다중 상속은 불가능하지만 하나의 클래스가 여러 인터페이스를 구현할 수 있다.
- 인터페이스에 구현 코드가 없어 모호하지 않기 때문이다.
- 디폴트 메서드가 인터페이스 간에 중복되는 경우에는 구현하는 클래스에서 재정의해주거나 한 인터페이스의 디폴트 메서드를 선택해서 사용해야한다.
- 여러 인터페이스를 구현한 클래스는 인터페이스 타입으로 형변환 되는 경우, 해당 인터페이스에 선언된 메서드만 사용할 수 있다.
Sell.java
package ch15;
public interface Sell {
void sell();
default void order() {
System.out.println("sell order");
}
}
Buy.java
Sell 인터페이스와 함께 order()라는 중복된 메서드를 가지고 있다.
package ch15;
public interface Buy {
void buy();
default void order() {
System.out.println("buy order");
}
}
Customer.java
- 인터페이스를 구현하는 클래스에서 반드시 default method를 재정의 해준다.
- 또는 Buy.super.order(); 과 같이 사용할 인터페이스의 default method를 선택할 수 있다.
package ch15;
public class Customer implements Buy, Sell{
@Override
public void buy() {
System.out.println("customer buy");
}
public void sell() {
System.out.println("custumer sell");
}
@Override
public void order() {
System.out.println("customer order"); // default method 재정의
// Buy.super.order();
}
public void hello() {
System.out.println("hello");
}
}
CustomerTest.java
- Buy 또는 Sell 인터페이스 형으로 형변환 한 인스턴스는 각 인터페이스에 정의된 메서드만을 사용할 수 있다.
- buyer와 seller 변수가 가리키고 있는 인스턴스는 customer 이므로 order() 메서드 실행 시 Customer 클래스에서 오버라이딩한 order()가 실행된다.
package ch15;
public class CustomerTest {
public static void main(String[] args) {
Customer customer = new Customer(); // Customer 클래스의 메서드 사용 가능
customer.buy();
customer.sell();
customer.order();
customer.hello();
Buy buyer = customer; // 묵시적 형변환
buyer.buy();
buyer.order(); // Customer의 order 실행 (가상 메서드)
Sell seller = customer;
seller.sell();
seller.order();
}
}
수행 결과
customer buy
custumer sell
customer order
hello
customer buy
customer order
custumer sell
customer order
인터페이스의 상속
- 인터페이스 간에도 상속을 할 수 있다.
- 인터페이스는 다중 상속이 가능하고 구현 코드의 상속이 아니므로 타입 상속이라고 한다.
- extends 뒤에 여러 개가 오면 인터페이스 상속이라고 생각하면 된다.
- 구현하는 클래스에서 상위, 하위 인터페이스에서 선언된 모든 메서드를 다 구현해주어야 한다.
클래스 상속과 인터페이스 구현 함께 쓰기
- 실무에서 프레임워크나 오픈소스와 함께 연동되는 구현을 하게 되면 상속과 여러 인터페이스의 구현을 같이 사용하는 경우가 많다.
- 앞에 extends로 상속 명시, 뒤에 implements로 구현 명시
예제 코드
클래스 다이어그램
- 책이 순서대로 대여되는 도서관 구현
- ArrayList를 활용해 책을 보관하는 자료구조 Shelf에 구현
- Queue 인터페이스 구현
- BookShelf 클래스는 Shelf 클래스를 상속받고 Queue 클래스를 구현한다.
Shelf.java
package ch15;
import java.util.ArrayList;
public class Shelf {
protected ArrayList<String> shelf;
public Shelf() {
shelf = new ArrayList<String>(); // 생성자에서 멤버 변수를 초기화 하는 것이 좋음
}
public ArrayList<String> getShelf() {
return shelf;
}
public int getCount() {
return shelf.size();
}
}
Queue.java
package ch15;
public interface Queue {
void enQueue(String title);
String deQueue();
int getSize();
}
BookShelf.java
package ch15;
public class BookShelf extends Shelf implements Queue{
@Override
public void enQueue(String title) {
shelf.add(title);
}
@Override
public String deQueue() {
return shelf.remove(0);
}
@Override
public int getSize() {
return getCount();
}
}
BookShelfTest.java
왜 for문으로 하면 3개만 찍히는지 모르겠다..
package ch15;
public class BookShelfTest {
public static void main(String[] args) {
Queue bookQueue = new BookShelf();
bookQueue.enQueue("토지1");
bookQueue.enQueue("토지2");
bookQueue.enQueue("토지3");
bookQueue.enQueue("토지4");
bookQueue.enQueue("토지5");
System.out.println(bookQueue.getSize());
System.out.println(bookQueue.deQueue());
System.out.println(bookQueue.deQueue());
System.out.println(bookQueue.deQueue());
System.out.println(bookQueue.deQueue());
System.out.println(bookQueue.deQueue());
// for ( int i = 0 ; i < bookQueue.getSize() ; i ++ ) {
// System.out.println(bookQueue.deQueue());
// }
}
}
수행 결과
5
토지1
토지2
토지3
토지4
토지5
'Java & Kotlin' 카테고리의 다른 글
[Java 객체지향] 추상 클래스와 템플릿 메서드 활용 예제 프로그램 (GameLevel 구현) (0) | 2022.02.02 |
---|---|
[Java 객체지향] 인터페이스의 요소 (0) | 2022.02.01 |
[Java 객체지향] 인터페이스 활용 (DAO 구현 해보기) (0) | 2022.02.01 |