[Java] 인터페이스

szlee·2023년 12월 15일
0

Java

목록 보기
22/23

< 김영한의 실전 자바 - 기본편 > 강의를 보고 이해한 내용을 바탕으로 합니다.





인터페이스

자바는 순수 추상 클래스를 더 편리하게 사용할 수 있는 인터페이스라는 기능을 제공한다.

순수 추상 클래스

public abstract class AbstractAnimal {
    public abstract void sound();
    public abstract void move();
}

인터페이스는 class가 아니라 interface 키워드를 사용하면 된다.

인터페이스

public interface class InterfaceAnimal {
    public abstract void sound();
    public abstract void move();
}

인터페이스는 public abstract 키워드 생략 가능

public interface class InterfaceAnimal {
    void sound();
    void move();
}

순수 추상 클래스의 특징

  • 인스턴스를 생성할 수 없다.
  • 상속 시 모든 메서드를 오버라이딩 해야한다.
  • 주로 다형성을 위해 사용된다.

인터페이스는 순수 추상 클래스의 특징과 같은데 여기에 편의 기능이 추가된다.

  • 인터페이스의 메서드는 모두 public, abstract이다.
  • 메서드에 public abstract를 생략할 수 있다. (생략 권장)
  • ⭐️인터페이스는 다중 구현을 지원한다.⭐️
  • 인터페이스에서 멤버 변수는 상수만 가능하다.(public static final)
    • 이 때 해당 키워드를 생략할 수 있다. (생략 권장)



public interface InterfaceAnimal {
    void sound(); //public abstract 생략
    void move();
}
public class Dog implements InterfaceAnimal{ //implements : 구현하겠략
    @Override
    public void sound() {
        System.out.println("멍멍");
    }

    @Override
    public void move() {
        System.out.println("개 이동");
    }
}
public class InterfaceMain {
    public static void main(String[] args) {
        //인터페이스 완전 추상이라 생성 불가
        //InterfaceAnimal interfaceAnimal = new InterfaceAnimal();

        Cat cat = new Cat();
        Dog dog = new Dog();
        Cow cow = new Cow();

        soundAnimal(cat);
        soundAnimal(dog);
        soundAnimal(cow);
    }

    //변하지 않는 부분
    private static void soundAnimal(InterfaceAnimal animal){
        System.out.println("동물 소리 테스트 시작");
        animal.sound();
        System.out.println("동물 소리 테스트 종료");
    }
}

클래스, 추상 클래스, 인터페이스는 모두 똑같다.

  • 클래스, 추상 클래스, 인터페이스는 프로그램 코드, 메모리 구조상 모두 똑같다. 모두 자바에서는 .class로 다루어진다. 인터페이스를 작성할 때도 .java에 인터페이스를 정의한다.
  • 자바에서는 순수 추상 클래스(모든 메서드 추상화) 대신 인터페이스라는 용어를 사용한다.
    • 따라서 인터페이스는 모든 메서드가 추상화된 순수 추상 클래스이고 각 메서드의 public abstract는 생략되어 있는 애다.


상속 vs 구현

부모 클래스의 기능을 자식 클래스가 상속 받을 때 클래스는 상속 받는다고 표현하지만, 부모 인터페이스의 기능을 자식이 상속 받을 때는 인터페이스를 구현한다고 표현한다.

이렇게 서로 다르게 표현하는 이유가 뭘까?
상속은 이름 그대로 부모의 기능을 물려 받는 것이 목적이다. 하지만 인터페이스는 모든 메서드가 추상 메서드이다. 따라서 물려받을 수 있는 기능이 없고 인터페이스에 정의한 모든 메서드를 자식이 오버라이딩 해서 기능을 구현해야 한다. 따라서 구현한다고 표현한다.
인터페이스는 메서드 이름만 있는 설계도이고, 이 설계도가 실제 어떻게 작동하는지는 하위 클래스에서 모두 구현해야한다. 따라서 인터페이스의 경우 상속이 아니라 해당 인터페이스를 구현한다고 표현한다.
(상속과 구현은 사람이 표현하는 단어만 다를 뿐 자바 입장에서는 똑같다. 일반 상속 구조와 동일하게 작동한다.)

인터페이스를 사용해야하는 이유?

모든 메서드가 추상 메서드인 경우 순수 추상 클래스를 만들어도 되고 인터페이스를 만들어도 된다. 그런데 왜 인터페이스를 사용해야 할까?
단순히 편리하다는 이유를 넘어서 다음과 같은 이유가 있다.

  • 제약 : 인터페이스를 만드는 이유는 인터페이스를 구현하는 곳에서 인터페이스의 메서드를 반드시 구현하라는 규약(제약)을 주는 것이다.
    그런데 순수 추상 클래스의 경우 미래에 누군가 그곳에 실행 가능한 메서드를 끼워 넣을 수 있다. 이렇게 되면 추가된 기능을 자식 클래스에서 구현하지 않을 수도 있고 또 더는 순수 추상 클래스가 아니게 된다. 인터페이스는 모든 메서드가 추상 메서드이기 때문에 이런 문제를 원천 차단할 수 있다.
  • 다중 구현 : 자바에서 클래스 상속은 부모를 하나만 지정할 수 있다. 반면에 인터페이스는 다중 구현이 가능하다.




인터페이스 - 다중 구현

클래스는 다중 상속이 안되는데 인터페이스의 다중 구현은 혀용되는 이유가 뭘까?
인터페이스는 모두 추상 메서드로 이루어져 있기 때문이다.


InterfaceA, InterfaceB는 둘 다 같은 methodCommon()을 가지고 있다.
그리고 Child는 두 인터페이스를 구현했다.
상속 관계의 경우 두 부모 중에 어떤 한 부모의 methodCommon()을 사용해야 할지 결정해야 하는 다이아몬드 문제가 발생한다.

하지만 인터페이스 자신은 구현을 가지지 않는다. 여기서 InterfaceA, InterfaceB는 같은 이름의 methodCommon()을 제공하지만 이것의 기능은 Child가 구현한다. 그리고 오버라이딩에 의해 어차피 Child에 있는 methodCommon()이 호출된다. 결과적으로 두 부모 중에 어떤 한 부모의 methodCommon()을 선택하는 것이 아니라 그냥 인터페이스들을 구현한 Child에 있는 methodCommon()이 사용된다. 이런 이유로 인터페이스는 다이아몬드 문제가 발생하지 않아 인터페이스의 경우엔 다중 구현을 허용한다.

public class Child implements InterfaceA, InterfaceB{
    @Override
    public void methodA() {
        System.out.println("Child.methodA");
    }

    @Override
    public void methodCommon() {
        System.out.println("Child.methodB");
    }

    @Override
    public void methodB() {
        System.out.println("Child.methodCommon");
    }
}
public class DiamondMain {
    public static void main(String[] args) {
        //InterfaceA a = new InterfaceA() ; //안됨
        InterfaceA a = new Child();
        a.methodA();
        a.methodCommon();

        InterfaceB b = new Child();
        b.methodB();
        b.methodCommon();
    }
}




클래스와 인터페이스 활용

클래스 상속과 인터페이스 구현을 함께 사용하는 예를 알아보자.

public class Bird extends AbstractAnimal implements Fly {
    @Override
    public void sound() {
        System.out.println("짹짹");
    }

    @Override
    public void fly() {
        System.out.println("새 날기");
    }
}

extends를 통한 상속은 하나만 할 수 있고 implements를 통한 인터페이스는 다중 구현할 수 있기 때문에 둘이 함께 나온 경우에 extends가 먼저 나와야 한다.


public class SoundFlyMain {
    public static void main(String[] args) {
        Dog dog = new Dog();
        Bird bird = new Bird();
        Chicken chicken = new Chicken();

        soundAnimal(dog);
        soundAnimal(bird);
        soundAnimal(chicken);

        flyAnimal(bird);
        flyAnimal(chicken);
    }

    //AbstractAnimal 사용 가능
    private static void soundAnimal(AbstractAnimal animal){
        System.out.println("동물 소리 테스트 시작");
        animal.sound();
        System.out.println("동물 소리 테스트 종료");
    }

    //Fly 인터페이스가 있으면 사용 가능
    private static void flyAnimal(Fly fly){
        System.out.println("날기 테스트 시작");
        fly.fly();
        System.out.println("날기 테스트 종료");
    }
}


profile
🌱

0개의 댓글