사이먼's 코딩노트

[Java] 인터페이스 본문

Java/Java

[Java] 인터페이스

simonpark817 2024. 3. 19. 10:19

[인터페이스]

1. 클래스 다중상속의 장점과 단점은?

  • 장점 : 객체에 다형성을 원하는 만큼 부여할 수 있다.
  • 단점 : 하나의 자식 클래스에 2명 이상의 부모 클래스에서 똑같은 형태의 메서드를 2개 이상을 물려받을 수 있다. 이 때, 자식 클래스에서 해당 메서드를 오버라이딩하지 않는다면, 모호함이 발생한다. 참고로 자식 클래스에서 해당 메서드를 오버라이딩할 의무는 없다.

2. Java에서 클래스 다중상속을 막은 이유는?

  • C++과 달리, Java는 개발자가 고생할 수 있는 여지를 줄이기 위해 해당 기능을 없앴다.

 

3. 클래스 다중상속에서 나타날 수 있는 문제점을 해결하는 방법은?

  • 자식 클래스에서 모호한 메서드를 오버라이딩한다.

 

4. 인터페이스와 클래스의 차이점은?

  • 인터페이스는 100% 추상클래스이다.
  • 인터페이스 안에 있는 메서드는 추상 메서드이기 때문에, abstract를 메서드 앞에 붙일 필요가 없다.

 

5. Java에서 인터페이스 다중상속을 허용한 이유는?

  • 객체지향 프로그래밍에서 다형성은 굉장히 중요하다.
  • Java에서는 다중상속이 제한되어 자유로운 다형성 부여가 힘든 상황이다.
  • 그래서 Java는 인터페이스라는 제한된 형태의 클래스는 다중상속을 허용하였다.
  • 인터페이스를 다중상속하여 모호함이 발생해도 자식 클래스에서 해당 메서드를 오버라이딩해야 하는 것이 필수이기 때문에 모호함이 존재할 수 없는 구조이다.

 

  • 다음은 표를 통해 구상(일반) 클래스, 추상 클래스, 인터페이스를 비교해보자

클래스 간 비교 표

 

  • 표에 작성되진 않았지만, 예외인 경우를 몇 가지 소개하고자 한다.
  • 먼저 인터페이스는 다른 인터페이스들을 다중상속할 수 있다.
  • 그리고 인터페이스에는 private를 사용할 수 없다.

 

[문제]

  • 이제는 문제를 통해서 인터페이스 역할에 대해서 더 자세히 살펴봅시다.
  • 문제는 Main 메서드를 보고 프로그램 실행 시 에러가 발생하지 않도록 클래스와 인터페이스를 추가해봅시다.
public class Main {
    public static void main(String[] args) {
        사람 a사람 = new 홍길동();
        변호사 a변호사 = (변호사)a사람;
        치과의사 a치과의사 = (치과의사)a사람;
        성화봉송자 a성화봉송자 = (성화봉송자)a사람;
    }
}

 

  • 다음은 클래스와 인터페이스를 추가한 코드이다.
public class Main {
    public static void main(String[] args) {
        사람 a사람 = new 홍길동();
        변호사 a변호사 = (변호사)a사람;
        치과의사 a치과의사 = (치과의사)a사람;
        성화봉송자 a성화봉송자 = (성화봉송자)a사람;
    }
}

class 사람 {

}

class 홍길동 extends 사람 implements 변호사, 치과의사, 성화봉송자 {

}

interface 변호사 {

}

interface 치과의사 {

}

interface 성화봉송자 {

}
  • 먼저 Main 메서드를 봤을 때 필요한 클래스는 사람, 홍길동, 변호사, 치과의사, 성화봉송자 이다.
  • a사람에는 홍길동 객체와 연결되는 리모콘이 포함되어 있다.
  • a변호사, a치과의사, a성화봉송자에는 a사람이 가지고 있는 홍길동과 연결되는 리모콘을 넣기 위해 강제 형변환을 하고 있다.
  • [사람 a사람 = new 홍길동()] 여기서 홍길동 클래스는 우선 사람 클래스에게 상속받는다는 것을 눈치챌 수 있다.
  • 그 다음은 홍길동 객체는 변호사, 치과의사, 성화봉송자 각 클래스와도 상속을 받지만 일반 클래스로는 다중상속이 불가능하다.
  • 다중상속이 가능하게 하기위해서 변호사, 치과의사, 성화봉송자를 인터페이스로 생성해주고, implements를 통해 다중상속을 해주면 해당 코드는 에러없이 잘 실행된다.

 

  • 다음은 다중상속과 메서드 오버라이딩 개념이 포함된 문제이다.
  • 마찬가지로 Main 메서드를 보고 프로그램 실행 시 에러가 발생하지 않도록 클래스와 인터페이스를 추가해봅시다.
public class Main {
    public static void main(String[] args) {
        사람 a사람 = new 사람();

        변호사 a변호사 = a사람;
        a변호사.변호하다();
        // 출력 : 사람이 변호 합니다.

        변호사 a변호사2 = new 오랑우탄();
        a변호사2.변호하다();
        // 출력 : 오랑우탄이 변호 합니다.

        의사 a의사 = new 오랑우탄();
        a의사.진찰하다();
        // 출력 : 오랑우탄이 진찰 합니다.

        의사 a의사2 = new 사람();
        a의사2.진찰하다();
        // 출력 : 사람이 진찰 합니다.
    }
}

 

  • 다음은 클래스와 인터페이스를 추가한 코드이다.
class 사람 implements 변호사, 의사 {
    public void 변호하다() {
        System.out.println("사람이 변호 합니다.");
    }
    public void 진찰하다() {
        System.out.println("사람이 진찰 합니다.");
    }
}

class 오랑우탄 implements 변호사, 의사 {
    public void 변호하다() {
        System.out.println("오랑우탄이 변호 합니다.");
    }
    public void 진찰하다() {
        System.out.println("오랑우탄이 진찰 합니다.");
    }
}

interface 변호사 {
    void 변호하다();
}

interface 의사 {
    void 진찰하다();
}
  • 먼저 Main 메서드를 봤을 때 필요한 클래스는 사람, 변호사, 의사, 오랑우탄 이다.
  • a사람에는 사람 객체와 연결된 리모콘이 포함되어 있다.
  • a변호사에는 a사람이 가지고 있는 사람과 연결되는 리모콘을 넣고 있고, 이를 통해 사람은 변호사에 상속받는다는 것을 알 수 있다.
  • 또한 a의사2를 보면 new 사람()을 통해 사람 객체와 연결되는 리모콘을 넣고 있고, 이 또한 사람은 의사에게도 상속받는 것을 알 수 있다.
  • 일반 클래스에서는 다중상속이 불가능하기 떄문에 인터페이스로 의사와 변호사를 생성해주고 implements를 통해 다중상속을 해줘야한다.
  • 각 변호사와 의사는 변호하다(), 진찰하다() 라는 메서드를 가지고 있고 인터페이스의 메서드는 100% 추상 메서드로 구성되기 때문에 void 변호하다(); void 진찰하다(); 라고 선언만 해주고 무조건 자식 클래스인 사람 클래스에서 오버라이딩을 해줘야하고 오버라이딩한 메서드 앞에는 반드시 public를 붙혀줘야한다.
  • 오랑우탄 클래스도 사람 클래스와 같은 역할을 하기 때문에 마찬가지로 인터페이스인 변호사와 의사를 상속받고 메서드 오버라이딩을 무조건 해줘야한다.

 

  • 다음은 비슷한 유형이지만 조금 심화된 인터페이스 문제이다.
  • 마찬가지로 Main 메서드를 보고 프로그램 실행 시 에러가 발생하지 않도록 코드를 완성해봅시다.
public class Main {
    public static void main(String[] args) {
        진찰하다(new 사람());
        // 출력 : 사람이 진찰합니다.
        진찰하다(new 원숭이());
        // 출력 : 원숭이가 진찰합니다.
        진찰하다(new 치타());
        // 출력 : 치타가 진찰합니다.
        진찰하다(new 기린());
        // 출력 : 기린이 진찰합니다.
        진찰하다(new 로봇());
        // 출력 : 로봇이 진찰합니다.
        진찰하다(new 삼성());
        // 출력 : 삼성이 진찰합니다.
    }

    public static void 진찰하다(의사 a의사) {
        a의사.진찰();
    }
}

 

 

  • 다음은 클래스와 인터페이스를 추가한 코드이다.
interface 의사 {
    public void 진찰();
}

class 사람 implements 의사 {
    public void 진찰() {
        진료();
    }
    void 진료() {
        System.out.println("사람이 진찰합니다.");
    }
}

class 원숭이 implements 의사 {
    public void 진찰() {
        진료();
    }
    void 진료() {
        System.out.println("원숭이가 진찰합니다.");
    }
}

class 치타 implements 의사 {
    public void 진찰() {
        진료();
    }
    void 진료() {
        System.out.println("치타가 진찰합니다.");
    }
}

class 기린 implements 의사 {
    public void 진찰() {
        진료();
    }
    void 진료() {
        System.out.println("기린이 진찰합니다.");
    }
}

class 로봇 implements 의사 {
    public void 진찰() {
        진료();
    }
    void 진료() {
        System.out.println("로봇이 진찰합니다.");
    }
}

class 삼성 implements 의사 {
    public void 진찰() {
        진료();
    }
    void 진료() {
        System.out.println("삼성이 진찰합니다.");
    }
}
  • 먼저 Main 클래스에서 진찰하다() 메서드를 살펴보면 매개변수로 (의사 a의사)을 받고있는 것으로 보아 메서드를 호출할 때 인자로 사람, 원숭이, 치타, 기린, 로봇, 삼성이라는 객체를 생성해서 넘길 때 a의사에 해당 객체의 리모콘을 넣고 있다는 것을 알 수 있다.
  • 그렇다면 a의사를 통해 진찰() 메서드를 호출하게 되면 각 연결된 객체에 맞게 출력문이 출력되야 한다.
  • 우리가 알 수 있는 것은 모든 클래스는 의사에게 상속을 받는다는 것이고 이 때 인터페이스를 사용한 이유는 다형성 때문이다.
  • 인터페이스와 implements를 통한 상속을 구현함으로써 각 클래스가 가진 고유의 메서드를 훼손하지 않고도 의사라는 객체가 다형성을 가질 수 있도록 하는 것이다.

 

반응형

'Java > Java' 카테고리의 다른 글

[Java] Stream  (0) 2024.05.02
[Java] 예외처리 / 접근제한자  (0) 2024.03.19
[Java] 제네릭 / HashMap / 정리  (0) 2024.03.18
[Java] ArrayList  (2) 2024.03.18
[Java] 배열 / toString / equals / StringBuilder  (2) 2024.03.14