처음부터 차근차근

[Java] Interface 본문

Language/Java

[Java] Interface

HangJu_95 2024. 1. 2. 15:36
728x90

Interface란?

interface는 일종의 추상클래스입니다.

추상메서드와 상수만을 멤버로 가질 수 있으며, 그 외의 다른 어떠한 요소도 허용하지 않습니다.

추상클래스를 '미완성 설계도'라고 한다면, 인터페이스는 밑그림만 그려져 있는 '기본 설계도'라고 할 수 있습니다.

interface 인터페이스이름 {
	public static final 타입 상수이름 = 값;
	public abstract 메서드이름(매개변수 목록);
}
  • 순스 추상 클래스의 특징을 모두 가집니다.
    • 인스턴스 생성 불가
    • 상속 시 모든 메서드를 오버라이딩 해야 한다.
    • 주로 다형성을 위해 사용된다.
  • 인터페이스에도 클래스처럼 접근제어자로 public 또는 default만 사용할 수 있다.
  • 모든 메서드는 public abstract이어야 하며, 이를 생략할 수 있습니다.
  • 모든 멤버 변수는 public static final이어야 하며, 이를 생략할 수 있습니다.
  • 인터페이스는 다중 구현(다중 상속)을 지원합니다.

간단한 예제

저번 추상 클래스에서 한 모델을 이번에는 인터페이스로 작성해보겠습니다.

참고 : 클래스 상속 관계는 UML에서 실선을 사용하지만, 인터페이스 구현(상속) 관계는 UML에서 점선을 사용한다.

package poly.ex5;

public interface InterfaceAnimal {
    void sound();
    void move();
}
package poly.ex5;

public class Dog implements InterfaceAnimal {
    @Override
    public void sound() {
        System.out.println("멍멍");
    }

    @Override
    public void move() {
        System.out.println("개 이동");
    }
}

인터페이스를 상속 받을 때는 extends 대신에 implements라는 구현이라는 키워드를 사용해야 합니다. 따라서 인터페이스는 상속이라 하지 않고 구현이라 합니다.

package poly.ex5;

public class Cat implements InterfaceAnimal {
    @Override
    public void sound() {
        System.out.println("야옹");
    }

    @Override
    public void move() {
        System.out.println("고양이 이동");
    }
}
package poly.ex5;

public class Caw implements InterfaceAnimal {
    @Override
    public void sound() {
        System.out.println("음머");
    }

    @Override
    public void move() {
        System.out.println("소 이동");
    }
}
package poly.ex5;

public class InterfaceMain {
    public static void main(String[] args) {
        //인터페이스 생성 불가
        //InterfaceAnimal interfaceMain1 = new InterfaceAnimal();

        Cat cat = new Cat();
        Dog dog = new Dog();
        Caw caw = new Caw();

        soundAnimal(cat);
        soundAnimal(dog);
        soundAnimal(caw);
    }
    //동물이 추가 되어도 변하지 않는 코드
    private static void soundAnimal(InterfaceAnimal animal) {
        System.out.println("동물 소리 테스트 시작");
        animal.sound();
        System.out.println("동물 소리 테스트 종료");
    }
}

 

저번 포스팅에서 설명한 순수 추상 클래스와 예제 거의 유사하며, 프로그램 코드, 메모리 구조상 모두 똑같습니다.

즉, 인터페이스는 순수 추상 클래스와 비슷하다고 생각하면 됩니다.

왜 구현인가??

  • 부모 클래스의 기능을 자식 클래스가 "상속" 받는다고 표현합니다. 즉, 이름 그대로 부모의 기능을 물려 받는 것이 목적입니다.
  • 인터페이스는 모든 메서드가 추상 메서드입니다. 따라서 물려받을 수 있는 기능이 없고, 오히려 인터페이스에 정의한 모든 메서드를 자식이 오버라디이 해서 기능을 구현해야 합니다. 따라서 구현이라고 표현합니다.

Interface를 사용해야 하는 이유

  • 제약
    • 인터페이스를 만드는 이유는, 인터페이스를 구현하는 곳에서 인터페이스의 메서드를 반드시 구현해라는 규약을 주는 것입니다.
    • 그러나 순수 추상 클래스의 경우, 누군가 그곳에 실행 가능한 메서드를 끼워 넣을 수 있습니다. 이렇게 되면 순수 추상 클래스가 아니게 됩니다. 따라서 interface는 이러한 문제를 원천 차단할 수 있습니다.
  • 다중구현
    • 자바에서 클래스 상속은 부모 하나만 지정할 수 있지만, 반면에 인터페이스는 부모를 여러명 두는 다중 구현(다중 상속)이 가능합니다.

Interface - 다중 구현

클래스는 다중 상속을 지원하지 않지만, 인터페이스는 다중 상속(다중 구현)을 지원합니다.

  • InterfaceA, B 모두 동일한 methodCommon()을 가지고 있습니다. 그리고 Child는 두 인터페이스를 구현했습니다. 상속 관계의 경우 두 부모 중에 어떤 한 부모의 methodCommon()을 사용해야 할 지 결정해야 하는 다이아몬드 문제가 발생합니다.
  • 하지만 인터페이스 자신은 구현을 가지지 않습니다. 대신 인터페이스를 구현한 곳에서 해당 기능을 모두 구현해야 합니다.
  • 동일한 메서드가 존재해도, 구현은 Child가 합니다. 그리고 오버라이딩에 의해 Child에 있는 methodCommon()이 호출됩니다.
  • 이런 이유로 인하여 인터페이스는 다이아몬드 문제가 발생하지 않으며, 다중 구현을 허용합니다.

(못 믿겠으면 직접 해봐야겠쥐~?)

package poly.diamond;

public interface InterfaceA {
    void methodA();
    void methodCommon();
}
package poly.diamond;

public interface InterfaceB {
    void methodB();
    void methodCommon();
}
package poly.diamond;

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

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

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

public class DiamondMain {
    public static void main(String[] args) {
        InterfaceA a = new Child();
        a.methodA();
        a.methodCommon();

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

interfaceA 타입인 경우
interfaceB 타입인 경우

인터페이스의 상속

인터페이스는 인터페이스로부터만 상속 받을 수 있으며, 클래스와는 달리 다중 상속이 허용됩니다.

interface Movable {
	void move(int x, int y);
}

interface Attackable {
	void attack(Unit u);
}

interface Fightable extends Movable, Attackable {}

클래스와 인터페이스 활용

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

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

package poly.ex6;

public abstract class AbstractAnimal {
    public abstract void sound();
    public void move() {
        System.out.println("동물이 이동합니다.");
    }
}
package poly.ex6;

public interface Fly {
    void fly();
}
package poly.ex6;

public class Dog extends AbstractAnimal {
    @Override
    public void sound() {
        System.out.println("멍멍");
    }
}
package poly.ex6;

public class Chicken extends AbstractAnimal implements Fly {
    @Override
    public void sound() {
        System.out.println("꼬끼오");
    }

    @Override
    public void fly() {
        System.out.println("닭 날기");
    }
}
package poly.ex6;

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

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

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);
//        flyAnimal(dog); // 개는 Fly를 구현하지 않았으므로 컴파일 에러
    }

    //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("날기 테스트 종료");
    }
}

Fly 인터페이스를 구현한 Bird, Chicken을 전달해서 flyAnimal을 실행할 수 있습니다.

인터페이스의 장점

  1. 개발 시간을 단축시킬 수 있습니다.
  2. 표준화가 가능합니다.
  3. 서로 관계없는 클래스들에게 관계를 맺어 줄 수 있습니다.
  4. 독립적인 프로그래밍이 가능합니다.
    • 선언과 구현을 분리시킬 수 있기 때문에 실제 구현에 독립적인 프로그램을 작성하는 것이 가능합니다.
참고
자바8에 등장한 default 메서드를 사용하면 인터페이스도 메서드를 구현할 수 있다. 하지만 이것은 예외적으로 아주 특별한 경우에만 사용해야 한다. 자바9에서 등장한 인터페이스의 private 메서드도 마찬가지이다. 지금 학습 단계에서는 이 부분들을 고려하지 않는 것이 좋다. 이 부분은 뒤에서 따로 다룬다.

참고

자바의 정석_기초편

 

김영한의 실전 자바 - 기본편 강의 - 인프런

실무에 필요한 자바 객체 지향의 핵심 개념을 예제 코드를 통해 쉽게 학습합니다., 국내 개발 분야 누적 수강생 1위, 제대로 만든 김영한의 실전 자바[사진][임베딩 영상]단순히 자바 문법을 안다

www.inflearn.com

 

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

[Java] abstract 추상 클래스 & 추상 메서드  (1) 2024.01.02
[Java] 다형성  (0) 2024.01.01
[Java] final  (1) 2023.12.31
[Java] static  (2) 2023.12.29
[Java] Java 메모리 구조  (1) 2023.12.29