티스토리 뷰

JAVA

[Java Study 08] 인터페이스

DevBee 2021. 5. 5. 06:10

인터페이스

인터페이스는 동일한 목적 하에 동일한 기능을 수행하게 강제하는 것으로 자바의 다형성을 극대화하여 개발 코드 수정을 줄이고 프로그램 유지보수성을 높이기 위해 사용합니다.

 

간단한 예로 인터페이스를 이해해보겠습니다.

 

동물원 사육사가 있다.
육식 동물이 들어오면 그 동물이 좋아하는 먹이를 던져준다.
호랑이가 들어오면 돼지고기를 던져주고
사자가 들어오면 닭고기를 던져준다.

위와 같은 케이스를 코드로 작성해보겠습니다.

 

Animal.java

public class Animal {
    private String name;

    public Animal(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

 

Tiger.java

public class Tiger extends Animal {
    public Tiger() {
        super("tiger");
    }
}

 

Lion.java

public class Lion extends Animal {
    public Lion() {
        super("lion");
    }
}

 

Zookeeper.java

public class Zookeeper {
    // Tiger가 들어오는 경우 돼지고기 주기
    public void feed(Tiger tiger) {
        System.out.println("feed pork");
    }

    // Lion이 들어오는 경우 닭고기 주기
    public void feed(Lion lion) {
        System.out.println("feed chicken");
    }

    public static void main(String[] args) {
        Zookeeper zookeeper = new Zookeeper();

        Tiger tiger = new Tiger();
        Lion lion = new Lion();

        zookeeper.feed(tiger);
        zookeeper.feed(lion);
    }
}

 

Zookeeper는 Tiger, Lion이 들어오는 경우 그 동물이 좋아하는 먹이를 주는 메소드를 가지고 있습니다. 이 클래스를 실행하면 다음과 같은 결과가 나옵니다.

 

feed pork
feed chicken

 

지금은 Tiger, Lion만 있지만 또 다른 육식 동물이 들어오는 경우 해당 동물이 좋아하는 먹이를 던져주는 메소드를 계속해서 추가 해야한다는 문제점이 있습니다.

 

이를 해결하기 위해 육식 동물 인터페이스를 생성하고 구현할 수 있습니다. 먼저 육식 동물 인터페이스를 정의합니다.

 

public interface Predator {

}

 

이 인터페이스를 Tiger, Lion과 같은 육식 동물이 구현합니다. 구현은 implements 키워드를 사용합니다. 인터페이스의 경우 다중 구현이 가능합니다. 즉, implements 키워드 뒤에 여러 개의 인터페이스가 와도 됩니다.

 

public class Tiger extends Animal implements Predator {
    public Tiger() {
        super("tiger");
    }
}

------------------------------------------------------------

public class Lion extends Animal implements Predator {
    public Lion() {
        super("lion");
    }
}

 

위와 같이 인터페이스를 구현하고 Zookeeper 클래스에서 먹이를 주는 메소드의 매개변수 타입을 인터페이스로 지정하면 Tiger, Lion을 모두 해당 인터페이스 타입으로 변환할 수 있기 때문에 추가적인 메소드가 필요하지 않습니다.

 

public class Zookeeper {
    // 육식 동물이 들어오는 경우 먹이주기
    public void feed(Predator predator) {
        System.out.println("feed pork");
    }

    public static void main(String[] args) {
        Zookeeper zookeeper = new Zookeeper();

        Tiger tiger = new Tiger();
        Lion lion = new Lion();

        zookeeper.feed(tiger);
        zookeeper.feed(lion);
    }
}

 

하지만 위 메소드에도 문제가 있습니다. 위와 같이 하나의 메소드를 사용하면 결과가 모두 같습니다. 따라서 각각의 육식 동물들이 자신이 먹는 먹이를 반환하는 메소드를 가질 수 있도록 수정해보겠습니다.

 

먼저 인터페이스 내에 메소드를 선언합니다.

 

public interface Predator {
    public String getFood();  // 메소드 선언(구현부 x)
}

 

이 메소드는 구현부가 없습니다. 따라서 이 인터페이스를 구현하는 클래스에서 반드시 이 메소드를 구현한 뒤 사용해야합니다. 이처럼 인터페이스를 사용하면 어떠한 기능을 여러 클래스에서 반드시 구현하도록 강제할 수 있습니다.

 

public class Tiger extends Animal implements Predator {
    public Tiger() {
        super("tiger");
    }

    @Override
    public String getFood() {
        return "pork";  // Tiger가 좋아하는 pork 반환
    }
}

-----------------------------------------------------------

public class Lion extends Animal implements Predator {
    public Lion() {
        super("lion");
    }

    @Override
    public String getFood() {
        return "chicken";  // Lion이 좋아하는 chicken 반환
    }
}

 

위와 같이 인터페이스에 메소드를 추가하고 구현 클래스에서 이 메소드를 구현하면 필요에 따라 원하는 기능을 하는 메소드를 호출할 수 있습니다. 따라서 Zookeeper 내 feed 메소드 또한 아래와 같이 수정하여 들어오는 육식동물이 좋아하는 음식을 던져줄 수 있도록 할 수 있습니다.

 

public class Zookeeper {
    public void feed(Predator predator) {
        System.out.println("feed " + predator.getFood());
    }

    public static void main(String[] args) {
        Zookeeper zookeeper = new Zookeeper();

        Tiger tiger = new Tiger();
        Lion lion = new Lion();

        zookeeper.feed(tiger);
        zookeeper.feed(lion);
    }
}

// 출력
// feed pork
// feed chicken

 

육식 동물들의 종류만큼의 feed 메소드가 필요했던 ZooKeeper 클래스를 Predator 인터페이스를 이용하여 구현했더니 단 한개의 feed 메소드로 구현이 가능해졌습니다. 여기서 중요한 점은 메소드의 갯수가 줄어들었다는 점이 아니라 ZooKeeper클래스가 동물들의 종류와 상관없는 독립적인 클래스가 되었다는 점입니다.

 

인터페이스 상속

일반적인 클래스는 다중 상속이 되지 않지만 인터페이스의 경우 여러 인터페이스를 상속 받을 수 있습니다.

 

public interface LandAnimal {
    public String liveIn();
}

--------------------------------

public interface MarineAnimal {
    public String liveIn();
}

--------------------------------

public interface Predator extends LandAnimal, MarineAnimal {
    public String getFood();
}

 

Predator 인터페이스를 구현하는 클래스(Tiger, Lion 등)의 경우 LandAnimal 또는 MarineAnimal의 liveIn() 메소드 또한 강제로 구현해야 합니다.

 

 

일반 클래스의 경우 다중 상속이 없는 이유는 다음과 같습니다.

 

 

만약 GrandFather 클래스에 A()라는 메소드가 있고 이 메소드를 FatherA, FatherB 클래스에서 모두 오버라이딩해서 사용하는 경우, Son 클래스에서 A () 메소드를 호출하게 되면 어떤 부모의 메소드를 호출한 것인지 알 수 없기 때문에 다중 상속을 지원하지 않습니다.

 

하지만 인터페이스의 경우는 다중 상속 받더라도 직접 인터페이스에서 구현하는 것이 아니기 때문에 다중 상속이 가능합니다.

 

자바 8 인터페이스

자바 8 이전까지는 인터페이스 내에 상수와 추상 메소드만 선언이 가능했습니다. 하지만 자바 8부터는 기본 메소드와 static 메소드가 추가되었습니다.

 

public interface 인터페이스명 {
    //상수 (명시하지 않아도 public static final)
    타입 상수명 = 값;

    //추상 메소드 (명시하지 않아도 public abstract)
    타입 메소드명(매개변수, ... );

    //디폴트 메소드
    default 타입 메소드명(매개변수, ... ){
        //구현부
    }
    
    //정적 메소드
    static 타입 메소드명(매개변수) {
        //구현부
    }
}

 

각각을 살펴보면 다음과 같습니다.

  • 상수: 인터페이스에서 초기값을 정한 뒤, 변경 없이 참조만 해서 사용 (절대적)
  • 추상 메소드: 선언만 해두고 오버라이딩해서 모두 구현 (강제적)
  • 디폴트 메소드: 기본적으로 선언과 구현을 하여 제공하지만 경우에 따라 오버라이딩으로 변경해서 사용 가능 (선택적)
  • 정적 메소드: 선언과 구현을 해서 제공하므로 변경없이 그대로 사용 (절대적)

 

기본 메소드 (Default Method)

인터페이스가 이미 구현되어 사용되고 있을 때 추가적인 메소드를 추상 메소드로 등록하게 되면 즉시 이를 구현한 모든 클래스에서 에러가 발생합니다. 따라서 이때 기본적으로 구현이 된 기본 메소드로 제공하면 에러 없이 추가 메소드를 각자 상황에 맞게 구현이 가능합니다.

 

static 메소드

static 메소드는 디폴트 메소드와 유사하지만 implements한 구현 클래스에서 override 할 수 없고 제공되는 그대로 사용해야 합니다. 또한 static 메소드이기 때문에 구현 클래스가 아닌 "인터페이스명.static메소드명"으로 접근해서 사용합니다.

 

자바 9 인터페이스

자바 9부터는 인터페이스 안에서 private 메소드를 사용할 수 있습니다. private 메소드는 인터페이스 내부에서 사용가능하며 다른 default 메소드나 static 메소드에서 사용하기 위해 작성된 메소드이기 때문에 해당 인터페이스를 구현한 클래스에서는 오버라이딩하여 사용할 수 없습니다.

 

public interface 인터페이스명 {
    // private 메소드
    private 타입 메소드명(매개변수, ... ){
        //구현부
    }
    
    // private static 메소드
    private static 타입 메소드명(매개변수) {
        //구현부
    }
}

 

인터페이스 vs 추상 클래스

추상 클래스는 일반 클래스와 큰 차이는 없지만 상속을 통해 자손 클래스에서 완성하도록 유도하는 미완성 설계도와 같은 클래스입니다. 따라서 추상 클래스만으로 객체를 생성할 수 없습니다.

 

인터페이스도 유사하게 기본 설계서라고 할 수 있습니다. 차이점은 추상 클래스와 다르게 다중 상속, 다중 구현이 가능하다는 것입니다.

 

추상 클래스와 인터페이스는 매우 유사하기 때문에 사용 용도에 따라 그 쓰임이 결정된다고 볼 수 있습니다.

 

1. 사용 용도

추상 클래스는 IS - A "~이다."

인터페이스는 HAS - A "~을 할 수 있는"

 

2. 공통된 기능 사용 여부

인터페이스를 사용하게 되면 공통된 기능들도 모두 구현 클래스에서 오버라이딩 해야 한다는 번거로움이 있었기 때문에 공통된 기능은 추상 클래스에서 구현하고 그 외 변경되는 내용만 추상 메소드 등으로 선언하여 서브 클래스에서 구현하도록 하는 방식을 사용할 수 있었습니다.

 

자바 8 이상부터는 default 메소드 등이 인터페이스에 추가되면서 이 방식으로 구분하는 것은 더 이상 의미가 없을 것 같습니다...

 

참고

- 인터페이스: wikidocs.net/217

- 인터페이스 상속: devlog-wjdrbs96.tistory.com/39

- 디폴트, static 메소드: www.hanumoka.net/2017/09/16/java-20170916-java8-interface/

- private 메소드: velog.io/@foeverna/Java-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4%EC%9D%98-%EC%9A%94%EC%86%8C%EB%93%A4

- 인터페이스와 추상 클래스: webdevtechblog.com/%EC%9E%90%EB%B0%94-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4%EC%99%80-%EC%B6%94%EC%83%81%ED%81%B4%EB%9E%98%EC%8A%A4-6eecbe5d6350 / myjamong.tistory.com/150

'JAVA' 카테고리의 다른 글

[Java Study 11] Enum  (0) 2021.05.11
[Java Study 10] 멀티쓰레드 프로그래밍  (0) 2021.05.07
[Java Study 07] 패키지  (0) 2021.05.04
[Java Study 06] 상속  (0) 2021.04.27
[Java Study 09] 예외(Exception) 처리  (0) 2021.04.26
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함