인터페이스 (interface)

박영준·2022년 12월 13일
0

Java

목록 보기
31/111

1. 정의

  • 일종의 추상 클래스

    참고: 상속 - 6) 추상 클래스

  • 개발 코드와 객체가 서로 통신하는 접점 역할

  • 객체의 사용 방법을 정의한 타입
    → 따라서, 데이터를 저장하는 인스턴스 or 정적 필드 선언 불가

  • 인터페이스는 구현은 없고, 어떤것을 가지고 있다는 것만 선언하는 것

2. 장점

  1. 개발 시간 단축

    • 메서드 호출하는 쪽은 선언부만 알면 된다.
    • 한쪽에서는 인터페이스 구현하는 클래스 작성하면, 이를 기다리지 않고 양쪽에서 동시 개발이 가능
  2. 표준화

    • 프로젝트에서의 기본 틀을 인터페이스로 작성한 후,
      각 개발자들에게 인터페이스를 구현해서 프로그램을 작성하도록 한다
  3. 관계없는 클래스 간에 관계 맺기

    • 상속관계가 아니지만, 관계를 맺어줄 수 있다
  4. 독립적인 프로그래밍

    • 인터페이스로, 선언과 구현을 분리시킬 수 있기 때문에,
      클래스 간의 관계를 간접적인 관계로 변경시킨다.
      (즉, 한 클래스의 변경이 다른 클래스에 영향 X)

3. 사용법

1) 인터페이스 선언

문법

interface 인터페이스이름 {
    public static final 타입 상수이름 =;
    public abstract 메서드이름(매개변수 목록);
}

예시

interface PlayingCard {
    public static final int SPACE = 4;
    final int DIAMOND = 3;				// public static final int DIAMOND = 34; 이 생략됨
    static int HEART = 2;				// public static final int HEART = 2; 이 생략됨
    int CLOVER = 1;						// public static final int CLOVER = 1; 이 생략됨
    
    public abstract String getCardNumber();
    String getCardKind();				// public abstract String getCardKind(); 이 생략됨
}
  • class 대신, interface를 사용

  • class 처럼, public/default 접근 제한자만 사용 가능

  • 모든 멤버변수 : public static final -> 생략 가능 (컴파일러가 자동 추가)

  • 모든 메서드 : public abstract -> 생략 가능 (컴파일러가 자동 추가)

2) 인터페이스 & 상속

Movable 인터페이스

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

Attackable 인터페이스

interface Attackable {
	void attack(Unit u);
}

Fightable 인터페이스

interface Fightable extends Movable, Attackable {		// move(int x, int y)와 attack(Unit u) 메서드를 멤버로 갖게 됨
}
  • 인터페이스는 인터페이스로부터만 상속받을 수 있음

  • 다중 상속이 가능함

3) 인터페이스 구현

(1) 구현 방법

  1. 개발 코드가 인터페이스를 호출하면,
  2. 인터페이스는 객체의 메소드를 호출한다.
  3. 객체는 인터페이스에서 정의된 추상 메소드와 (메소드 이름, 매개 타입, 리턴 타입이) 동일한 실체 메소드를 가지고 있어야 한다.

이러한 객체를 구현 객체
구현 객체를 생성하는 클래스를 구현 클래스

예시 1

인터페이스

public interface RemoteControl {

	//상수 필드
	public int MAX_VOLUME = 10;
	public int MIN_VOLUME = 0;
    
    //추상 메소드
    //public abstract 리턴타입 메소드이름 (매개변수, ...);
    public void turnOn();		//void 는 리턴값이 필요없음
    public void turnOff();
    public void setVolume(int volume)();

구현 클래스 1

//public class 구현클래스이름 implements 인터페이스이름
public class Television implements RemoteControl {

    //필드
    private int volume;

    //turnOn() 추상 메소드의 실체 메소드
    public void turnOn() {
        System.out.println("Tv를 켭니다.");
    }

    //turnOff() 추상 메소드의 실체 메소드
    public void turnOff() {
        System.out.println("Tv를 끕니다.");
    }

    //setVolume() 추상 메소드의 실체 메소드
        //인터페이스의 상수를 이용해서 volume 필드값을 제한
    public void setVolume(int volume) {
        if (volume > RemoteConrole.MAX_VOLUME) {
            this.volume = RemoteControl.MAX_VOLUME;

        } else if (volume < RemoteControl.MIN_VOLUME) {
            this.volume = RemoteControl.MIN_VOLUME;

        } else {
            this.volume = volume;
        }

        System.out.println("현재 TV 볼륨: " + this.volume);
    }
}

구현 클래스 2

//public class 구현클래스이름 implements 인터페이스이름
public class Audio implements RemoteControl {

    //필드
    private int volume;

    //turnOn() 추상 메소드의 실체 메소드
    public void turnOn() {
        System.out.println("Audio를 켭니다.");
    }

    //turnOff() 추상 메소드의 실체 메소드
    public void turnOff() {
        System.out.println("Audio를 끕니다.");
    }

    //setVolume() 추상 메소드의 실체 메소드
        //인터페이스의 상수를 이용해서 volume 필드값을 제한
    public void setVolume(int volume) {
        if (volume > RemoteConrole.MAX_VOLUME) {
            this.volume = RemoteControl.MAX_VOLUME;

        } else if (volume < RemoteControl.MIN_VOLUME) {
            this.volume = RemoteControl.MIN_VOLUME;

        } else {
            this.volume = volume;
        }

        System.out.println("현재 Audio 볼륨: " + this.volume);
    }
}

예시 2

인터페이스

public interface Tv {
    public int MIN_VOLUME = 0;
    public int MAX_VOLUME = 100;

    public void turnOn();
    public void turnOff();
    public void changeVolume (int volume);
    public void changeChannel (int channel);
}
  • 구현은 없고, 어떤것을 가지고 있다는 것만 선언

구현 클래스

public class LedTv implements Tv {

    @Override
    public void turnOn() {
        System.out.println("켜다");
    }

    @Override
    public void turnOff() {
        System.out.println("끄다");
    }

    @Override
    public void changeVolume(int volume) {
        System.out.println("볼륨 조정");
    }

    @Override
    public void changeChannel(int channel) {
        System.out.println("채널 조정");
    }
}
  • 구현 클래스 : Tv 가 가진 모든 기능을 LedTv도 사용할 수 있게 할 것이다라는 것
    • 따라서, Tv가 가지는 모든 기능들을 반드시 구현해야 함

테스트

public class LedExam {

    public static void main(String[] args) {
        Tv tv = new LedTv();
        tv.turnOn();
        tv.changeChannel(8);
        tv.changeVolume(39);
        tv.turnOff();
    }
}

(2) 객체 생성

구현 클래스가 작성되면, new 연산자로 객체 생성이 가능하다.

단,
인터페이스 변수 선언 이후에 구현 객체를 대입해야한다.

public class RemoteControlExample {
    public static void main(String[] args) {

		//인터페이스 RemoteControl 의 변수 rc 를 선언
        RemoteControl rc;

		//변수 rc 에 구현 객체 Television, Audio 생성
        rc = new Television();
        rc = new Audio();
        
        //객체 생성의 잘못된 예시
        Television tv = new Television();
    }
}

5) 다중 인터페이스

인터페이스 1

public interface RemoteControl {

	//상수 필드
	public int MAX_VOLUME = 10;
	public int MIN_VOLUME = 0;
    
    //추상 메소드
    //public abstract 리턴타입 메소드이름 (매개변수, ...);
    public void turnOn();		//void 는 리턴값이 필요없음
    public void turnOff();
    public void setVolume(int volume)();

인터페이스 2

public interface Searchable {
    void search (String url);
}

구현 클래스

public class SmartTelevision implements RemoteControl, Searchable {
    private int volume;

    //turnOn() 추상 메소드의 실체 메소드
    public void turnOn() {
        System.out.println("Tv를 켭니다.");
    }

    //turnOff() 추상 메소드의 실체 메소드
    public void turnOff() {
        System.out.println("Tv를 끕니다.");
    }

    //setVolume() 추상 메소드의 실체 메소드
    //인터페이스의 상수를 이용해서 volume 필드값을 제한
    public void setVolume(int volume) {
        if (volume > RemoteConrole.MAX_VOLUME) {
            this.volume = RemoteControl.MAX_VOLUME;

        } else if (volume < RemoteControl.MIN_VOLUME) {
            this.volume = RemoteControl.MIN_VOLUME;

        } else {
            this.volume = volume;
        }

        System.out.println("현재 TV 볼륨: " + this.volume);
    }

    //Searchable() 추상 메소드의 실체 메소드
    public void search (String url) {
        System.out.println(url + " 을 검색합니다.");
    }
}

6) 인터페이스 사용

인터페이스

public interface RemoteControl {

	//상수 필드
	public int MAX_VOLUME = 10;
	public int MIN_VOLUME = 0;
    
    //추상 메소드
    //public abstract 리턴타입 메소드이름 (매개변수, ...);
    public void turnOn();		//void 는 리턴값이 필요없음
    public void turnOff();
    public void setVolume(int volume)();

구현 클래스 1

//public class 구현클래스이름 implements 인터페이스이름
public class Television implements RemoteControl {

    //필드
    private int volume;

    //turnOn() 추상 메소드의 실체 메소드
    public void turnOn() {
        System.out.println("Tv를 켭니다.");
    }

    //turnOff() 추상 메소드의 실체 메소드
    public void turnOff() {
        System.out.println("Tv를 끕니다.");
    }

    //setVolume() 추상 메소드의 실체 메소드
        //인터페이스의 상수를 이용해서 volume 필드값을 제한
    public void setVolume(int volume) {
        if (volume > RemoteConrole.MAX_VOLUME) {
            this.volume = RemoteControl.MAX_VOLUME;

        } else if (volume < RemoteControl.MIN_VOLUME) {
            this.volume = RemoteControl.MIN_VOLUME;

        } else {
            this.volume = volume;
        }

        System.out.println("현재 TV 볼륨: " + this.volume);
    }
}

구현 클래스 2

//public class 구현클래스이름 implements 인터페이스이름
public class Audio implements RemoteControl {

    //필드
    private int volume;

    //turnOn() 추상 메소드의 실체 메소드
    public void turnOn() {
        System.out.println("Audio를 켭니다.");
    }

    //turnOff() 추상 메소드의 실체 메소드
    public void turnOff() {
        System.out.println("Audio를 끕니다.");
    }

    //setVolume() 추상 메소드의 실체 메소드
        //인터페이스의 상수를 이용해서 volume 필드값을 제한
    public void setVolume(int volume) {
        if (volume > RemoteConrole.MAX_VOLUME) {
            this.volume = RemoteControl.MAX_VOLUME;

        } else if (volume < RemoteControl.MIN_VOLUME) {
            this.volume = RemoteControl.MIN_VOLUME;

        } else {
            this.volume = volume;
        }

        System.out.println("현재 Audio 볼륨: " + this.volume);
    }
}

개발 코드

public class Myclass {

    //1. 필드
    RemoteControl rc = new Television();

    //2. 생성자
    Myclass (RemoteControl rc) {
        this.rc = rc;

        rc.turnOn();
        rc.setVolume(5);
    }

    //4. 메소드
    void methodA() {
    
    	3. 로컬 변수
        RemoteControl rc = new Audio();
        rc.turnOn();		//구현 클래스 2 Audio 의 turnOn() 실행
        rc.setVolume(5);		//구현 클래스 2 Audio 의 setVolume() 실행
    }

	//4. 메소드
    void methodB (RemoteControl rc) {
        rc.turnOn();
        rc.setVolume(5);
    }
}

실행 코드

public class MyclassExample {
    public static void main(String[] args) {

        //1. 인터페이스를 필드에서 사용할 경우
        MyClass myClass1 = new Myclass();
        myClass1.rc.turnOn();		//구현 클래스 1 Television 의 메소드 turnOn() 실행
        myClass1.rc.setVolume(5);		//구현 클래스 1 Television 의 메소드 setVolume() 실행 --> 값 5를 대입

        //2. 인터페이스를 생성자에서 사용할 경우
        MyClass myClass2 = new MyClass(new Audio());	//개발 코드의 MyClass 객체 생성한 경우, 구현 클래스 2 Audio 의 turnOn(), setVolume() 실행

        //3. 인터페이스를 로컬 변수에서 사용할 경우
        MyClass myClass3 = new MyClass();
        myClass3.methodA();

        //4. 인터페이스를 메소드에서 사용할 경우
        MyClass myClass4 = new MyClass();
        myClass4.methodB(new Television());		//구현 클래스 1 Television 의 메소드 turnOn(), setVolume() 실행
    }
}

4. 인터페이스 VS 추상 클래스

공통점

1) "설계도"

다른 클래스 작성에 도움을 주는 목적으로 작성되었다.

2) 추상 메서드

추상 메서드를 사용할 수 있다.

차이점

1) extends vs implements

인터페이스

interface 인터페이스이름 {
    public static final 타입 상수이름 =;
    public abstract 메서드이름(매개변수 목록);
}
  • implements 를 사용

추상 클래스

abstract class 클래스이름 {
    ...    
    public abstract void 메서드이름();    
}
  • extends 를 사용

상속과 구현을 동시에 할 경우

class Firgher extends Unit implements Fightable {
	public void move(int x, int y) {
    }

    public void attack(Unit u) {
    }
}

2) "설계도"

인터페이스

  • "기본 설계도"
    → 다중 상속(구현)이 가능

추상 클래스

  • "미완성 설계도"
    → '추상 메서드를 선언하여, 상속을 통해서 자손 클래스에서 완성하도록 유도하는 클래스' 이기 때문

3) 사용 의도

인터페이스

  • HAS - A "~ 을 할 수 있는"

  • 다중 상속의 가능 여부에 따라 용도를 구분

    • Java 는 한 개의 클래스만 상속이 가능
      → 추상 클래스의 상속을 통해, 클래스들을 구분
      → 인터페이스를 통해, 할 수 있는 기능들을 구현

추상 클래스

  • IS - A "~ 이다"

  • 다중 상속의 가능 여부에 따라 용도를 구분

    • Java 는 한 개의 클래스만 상속이 가능
      → 추상 클래스의 상속을 통해, 클래스들을 구분
      → 인터페이스를 통해, 할 수 있는 기능들을 구현

4) 공통된 기능의 사용 여부

인터페이스

  • 모든 클래스가 인터페이스를 사용해서 기본 틀을 구성한다면,
    공통으로 필요한 기능들도 모든 클래스에서 오버라이딩하여 재정의 해야하는 번거로움이 있다.

  • 인터페이스 사용 시기
    : 상속 관계를 쭉 타고 올라갔을때 다른 조상클래스를 상속하는데 같은 기능이 필요할 경우 인터페이스 사용
    (ex. Swimable)

추상 클래스

  • 추상 클래스를 통해, 일반 메서드를 작성하여 공통된 기능을 자식 클래스에서 사용할 수 있도록 구성하는 것은 불가능하다.
    Java 는 하나의 클래스만 상속이 가능하기 때문이다.

  • 추상클래스 사용 시기
    : 상속 관계를 쭉 타고 올라갔을때 같은 조상클래스를 상속하는데 기능까지 완변히 똑같은 기능이 필요한 경우
    (ex. attack, printInfo)

5. 인터페이스 VS 추상 클래스 VS 일반 클래스

  • 인간과 동물은 생명체를 상속
  • 각 생명체들은 구분에 따라 인간과 동물을 상속
  • 각각 할 수 있는 기능들을 인터페이스로 구현

Creature 추상클래스

public abstract class Creature {
    private int x;
    private int y;
    private int age;
    
    public Creature(int x, int y, int age) {
        this.age = age;
        this.x = x;
        this.y = y;
    }
    
    // 생명체(Creature)라면 나이를 먹는다. → 공통적인 기능 → 하위 클래스에서 상속할 수 있도록 일반 메서드로 구현
    public void age() {
        age++;
    }
    
    // 생명체(Creature)라면, x좌표상으로 이동 할 수 있다. → 공통적인 기능 → 하위 클래스에서 상속할 수 있도록 일반 메서드로 구현
    public void move(int xDistance) {
        x += xDistance;
    }
    
    public int getX() {
        return x;
    }
    
    public void setX(int x) {
        this.x = x;
    }
    public int getY() {
        return y;
    }
    public void setY(int y) {
        this.y = y;
    }
    
    // 모든 생명체에 필요한 기능이지만, 각각 생명체에 따라 다른 기능으로 구현해야 함 → 추상 메서드로 선언하여 하위 클래스에서 처리
    public abstract void attack();		// 공격하는 기능
    public abstract void printInfo();		// 정보를 출력하는 기능
    
    // toString() 메서드 : 나중에 출력은 간편하게하기 위해서 오버라이딩하여 사용
    @Override
    public String toString() {
        return "x : " + x + ", y : " + y + ", age : " + age;
    }
}

Animal 추상클래스

// 동물(Animal)은 생명체(Creature)이기 때문에,  Creature 추상클래스를 상속한다.
public abstract class Animal extends Creature {
    
    public Animal(int x, int y, int age) {
        super(x, y, age);
    }
    
    // 동물은 공격하는 기능을 가지기 때문에, 추상 메서드 attack()를 오버라이딩
    @Override
    public void attack() {
        System.out.println("몸을 사용하여 공격!!");
    }
}
  • Creature 추상클래스에서 상속받은 추상 메소드 printInfo() 는 어디있을까?
    • 생명체 클래스도 동물 클래스도 추상클래스이다. (추상클래스는 상속을 위한 클래스이기 때문)
    • 상위 클래스(생명체 클래스)에서 선언한 추상 메서드(printInfo())를 앞으로 동물 클래스를 상속할 클래스에게 위임한 것

Human 추상클래스

// Talkable 인터페이스를 구현 → 동물 클래스와의 차이점
public abstract class Human extends Creature implements Talkable{
    public Human(int x, int y, int age) {
        super(x, y, age);
    }
    
    // 추상 메서드 attack()
    @Override
    public void attack() {
        System.out.println("도구를 사용!!");
    }
    
    // 추상 메서드 talk()
    @Override
    public void talk() {
        System.out.println("사람은 말을 할 수 있다.");
    }
}
  • (Creature 추상클래스에서 상속받은 추상 메소드 printInfo() 는 Animal 추상클래스에서처럼 동일하게 아래로 위임함)

Talkable 인터페이스

public interface Talkable {
    abstract void talk();
}
  • Human 추상클래스에서 사용할 추상 메서드 talk() 하나만 선언

Pigeon 일반클래스

public class Pigeon extends Animal implements Flyable {
    public Pigeon(int x, int y, int age) {
        super(x, y, age);
    }
    
    // 추상 메서드 fly()
    @Override
    public void fly(int yDistance) {
        setY(getY() + yDistance);
    }
    
    // 추상 메서드 flyMove()
    @Override
    public void flyMove(int xDistance, int yDistance) {
        setY(getY() + yDistance);
        setX(getX() + xDistance);
    }
    
    // Creature 추상 클래스의 추상 메서드 printInfo()를 오버라이딩 (Human, Animal 추상 클래스에서도 아래로 위임했던 것)
    @Override
    public void printInfo() {
        System.out.println("Pigeon -> " + toString());
    }
}

동물 클래스를 상속하고 날수 있는 동물이기에 Flyable 인터페이스를 구현해주고 해당 메서드들을 구현 해 주었습니다.

Flyable 인터페이스

public interface Flyable {
    void fly(int yDistance);
    void flyMove(int xDistance, int yDistance);
}
  • Pigeon 일반클래스에서 사용할 추상 메서드 fly(), flyMove() 선언
    → (다른 동물들과는 달리) y행으로 위로 올라갈 수 있도록 하는 메서드들을 선언

Turtle 일반클래스

public class Turtle extends Animal implements Swimable{
    public Turtle(int x, int y, int age) {
        super(x, y, age);
    }
    
    @Override
    public void swimDown(int yDistance) {
        setY(getY() - yDistance);
    }
    
    // Creature 추상 클래스의 추상 메서드 printInfo()를 오버라이딩 (Human, Animal 추상 클래스에서도 아래로 위임했던 것)
    @Override
    public void printInfo() {
        System.out.println("Turtle -> " + toString());
    }
}

Kevin 일반클래스

public class Kevin extends Human implements Programmer, Swimable{
    public Kevin(int x, int y, int age) {
        super(x, y, age);
    }
    
    @Override
    public void coding() {
        System.out.println("Hello World!");
    }
    
    // 추상 메서드 swimDown() 를 오버라이딩 + 추가적인 조건(기능)으로 재정의("y값이 -10이하로 내려가면 죽을 수도 있다.") → 추상클래스 보다는 인터페이스를 사용
    @Override
    public void swimDown(int yDistance) {
        setY(getY() - yDistance);
        if(getY() < -10) {
            System.out.println("너무 깊이 들어가면 죽을수도 있어!!");
        }
    }
    
    // Creature 추상 클래스의 추상 메서드 printInfo()를 오버라이딩 (Human, Animal 추상 클래스에서도 아래로 위임했던 것)
    @Override
    public void printInfo() {
        System.out.println("Kevin -> " + toString());
    }
}
  • Kevin은 수영도 할 수 있고 코딩도 할 수 있다.
    → 인터페이스의 다중구현을 활용
    → Programmer와 Swimable 인터페이스를 모두 구현하고 필요한 메서드를 재정의

Programmer 인터페이스

public interface Programmer {
    void coding();
}

Swimable 인터페이스

public interface Swimable {
    void swimDown(int yDistance);
}
  • 공통된 기능 사용여부와 관련된 인터페이스

  • 거북이는 동물, 케빈은 사람
    → 두 생명체 모두 수영을 할 수 있지만(공통된 기능), 동물/사람 중 수영을 못하는 동물/사람이 있을 수도 있다.
    → 인터페이스로 따로 선언을 해줘서, 각각 수영을 할 수 있는 클래스에 구현
    → 가독성 ↑ + 유지보수 ↑


Main 실행클래스

public class Main {
    public static void main(String[] args) {
        Pigeon p = new Pigeon(5,10,14);
        p.printInfo();
        p.age();
        p.move(100);
        p.printInfo();
        p.fly(5);
        p.printInfo();
        p.flyMove(10, 20);
        p.printInfo();
        p.attack();
        System.out.println();
        
        Kevin kev = new Kevin(0, 0, 35);
        kev.printInfo();
        kev.age();
        kev.move(10);
        kev.printInfo();
        kev.attack();
        kev.coding();
        kev.swimDown(20);
        kev.printInfo();
        kev.talk();
        System.out.println();
        
        Turtle tur = new Turtle(100, -10, 95);
        tur.printInfo();
        tur.age();
        tur.move(-100);
        tur.printInfo();
        tur.attack();
        tur.swimDown(1000);
        tur.printInfo();
    }
}

실행 결과


참고: [JAVA] 추상클래스 VS 인터페이스 왜 사용할까? 차이점, 예제로 확인

profile
개발자로 거듭나기!

0개의 댓글