Java 다형성 - 인터페이스

자바 기능 중 순수 추상 클래스와 함께 인터페이스를 제공한다.
순수 추상 클래스와 인터페이스가 비슷해보이나, 몇 가지 차이와 편의 기능이 다르다.

순수 추상 클래스 코드

순수 추상 클래스 선언하는 컨벤션은 다음과 같다.

public abstract class AbsAnimal {
    public abstract String sound();
    public abstract String move();
}

순수 추상 클래스 특징

  • 인스턴스를 생성할 수 없다.
  • 상속한 경우 모든 메소드 오버라이딩이 필수이다.
  • 다형성 패턴에서 사용한다.

인터페이스

인터페이스 선언하는 컨벤션은 다음과 같이 class 대신 interface 이름이 들어간다.

public interface IAnimal {
    String sound();
    String move();
}

인터페이스 특징

  • 인터페이스 메소드는 public 과 abstract 으로 이루어져 있다.
  • 메소드는 추상 메소드를 의미하는 public abstract 생략할 수 있다. (권장)
  • 인터페이스 다중 상속(구현)을 지원한다.

인터페이스 멤버 변수

예제에서 정적 멤버 변수를 PI 선언한 것을 살펴보도록 한다

public interface IShape {
    public static final int MATH_PI = 3.14;
}

인터페이스에서 멤버 변수는 public, static, final 키워드 모두 포함하는 것으로 간주한다. 변수를 한번 초기화하면 더 이상 수정 불가. 이러한 성질을 상수라 한다.
그리고 상수 이름은 관례상 대문자에 언더스코어(_) 로 구분한다.

public interface IShape {
    int MATH_PI = 3.14;
}

public, static, final 생략해도 선언이 되지만, public, static, final 선언한 것 같이 동작한다.


interface 코드 예제

interface 예제 코드를 그림과 같이 구성한다.

IAnimal.java

package sound;

public interface IAnimal {
    String sound();
    String move();
}

인터페이스 선언은 class 대신 interface 이름으로 선언한다.
sound(), move() 메소드 이름 앞에는 public abstract 키워드가 생략되어있으나, 상속 받는 자는 모든 메소드를 메소드 오버라이딩이 필수이다.

Bird.java

package sound;

public class Bird implements IAnimal { // 컴파일 오류
    @Override
    public String sound() {
        return "짹짹";
    }

    @Override
    public String move() {
        return "참새 이동하기~";
    }
}

Cat.java

package sound;

public class Cat implements IAnimal {

    @Override
    public String sound() {
        return "냐옹";
    }

    @Override
    public String move() {
        return "야옹이 이동하기";
    }
}

Dog.java

package sound;

public class Dog implements IAnimal {
    @Override
    public String sound() {
        return "멍멍";
    }

    @Override
    public String move() {
        return "멍멍이 이동하기";
    }
}

IAnimalMain.java

package sound;

public class IAnimalMain {
    public static void main(String[] args) {
        IAnimal bird = new Bird();
        IAnimal cat = new Cat();
        IAnimal dog = new Cat();

        soundAnimal(bird);
        soundAnimal(cat);
        soundAnimal(dog);

        moveAnimal(bird);
        moveAnimal(cat);
        moveAnimal(dog);
    }

    private static void moveAnimal(IAnimal dog) {
        System.out.println(dog.move());
    }

    private static void soundAnimal(IAnimal animal) {
        System.out.println("동물 소리: " + animal.sound());
    }


}

실행 결과

동물 소리: 짹짹
동물 소리: 냐옹
동물 소리: 냐옹
참새 이동하기~
야옹이 이동하기
야옹이 이동하기

순수 추상 클래스가 인터페이스로 전환 되었을 뿐 순수 추상 클래스와 인터페이스 동작이 같다.

클래스, 추상 클래스, 인터페이스

  • 클래스 추상클래스 인터페이스의 프로그램 코드, 메모리 구조도 같다.
  • 자바에서 일반적인 클래스는 .class 관리되고 인터페이스를 작성할 때는 .java 인터페이스로 정의한다.
  • 인터페이스는 순수 추상 클래스와 유사한 것이다.

상속 과 구현

부모 클래스의 구현한 기능을 자식 클래스가 상속 받아 물려준 기능을 사용하지만, 인터페이스는 부모의 기능을 상속 받아 자식에서 구현한다.

상속은 이름 그대로 부모의 기능을 물려 받는 것이 목적이고,
다만 인터페이스는 정의한 메소드가 추상이므로 부모에서 기능을 구현할 수 없다.

인터페이스는 상속 받은 자식에서 물려 받은 기능을 오버라이딩하여 구현하기에 Implements(구현)이라는 단어를 사용한다.

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

모든 메소드가 추상 메소드인 경우 순수 추상 클래스를 만들어도 된다.
또는 인터페이스로 순수 추상 클래스처럼 만들어도 무방하다.
그렇다면 abstract 이 아닌 interface 를 사용 하는 것일까?

  • 제약: 인터페이스는 메소드를 자식에서 반드시 구현하는 조건이 있었다.
    미래의 누군가 자식 클래스에서 만들지 않을 수 있고, 순수 추상 클래스에서 메소드를 추가하여 사용하는 경우도 생길 것이다.
    이러한 경우의 수를 막아 인터페이스가 문제를 원천적으로 차단한다.
  • 다중 구현: 상속은 부모를 하나만 지정 가능하다. 인터페이스는 여러 부모를 둘 수 있으므로 다중 구현할 수 있다.
💡
인터페이스 메소드를 특별한 경우 기능을 구현할 수 있다.
자바 8 부터는 특별한 경우 public 메소드에서 기능을 구현할 수 있다.
자바 9 부터는 특별한 경우 private 메소드에서 기능을 구현할 수 있다.