코린이의 소소한 공부노트

인터페이스 선언, 상속, 구현 본문

Java

인터페이스 선언, 상속, 구현

무지맘 2022. 5. 13. 14:24

시간을 나타내는 Time 클래스가 아래 코드와 같고, Time 객체를 2개 만들었다고 해보자.

class Time{
    private int hour; // 0시 ~ 23시
    private int minute; // 0초 ~ 59분
    private int second; // 0초 ~ 59초
    
    public int getHour() { return hour; }
    // 이하 생략
}

캡슐화 - iv를 메서드가 감싸고 있는 형태

이때 한 객체에서 다른 객체의 hour값을 가지고 올 때, 메서드를 통해서 iv에 접근해야 한다.

t.hour; // No
t.getHour(); // OK

캡슐화를 하는 이유는 데이터를 보호하기 위함이다. 그래서 위 코드의 첫 줄같이 직접 iv에 접근하는 것이 아닌 메서드를 통해 접근해야 한다. 이때 객체의 가장 바깥 껍데기(메서드를 감싸는 부분)가 인터페이스라고 보면 된다. 지금부터 인터페이스가 무엇인지, 어떻게 작성해서 어떻게 사용하는지 알아보자.

 

인터페이스

1. 추상 메서드의 집합으로, 구현된 것이 전혀 없는 설계도라고 볼 수 있다.

interface 이름{
    public static final 타입 상수이름 = 값;
    public abstract 반환타입 메서드이름(매개변수);
}

 1) 변수, iv, cv를 가질 수 없다.

  - 상수는 가능하다.

 2) 추상 클래스와의 공통점은 추상 메서드를 가지고 있다는 것이다.

 3) 추상 클래스와의 차이점은

  - 추상 클래스: 추상 메서드를 갖고 있는 일반 클래스. 생성자, iv, im 선언 가능. 구현 키워드는 extends

  - 인터페이스: 추상 메서드의 집합. 생성자, iv, im 불가능. 구현 키워드는 implements. 부수적으로 상수, static 메서드, default 메서드는 선언 가능

2. 모든 멤버가 public인 껍데기라고도 설명할 수 있다.

interface PlayingCard{
    // 상수
    public static final int SPADE = 4;
    final int DIAMOND = 3;	// public static final int DIAMOND = 3;
    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();
}

 1) 상수와 추상 메서드만 선언 가능하다.

  - 껍데기라고 표현한 이유는 구현부가 전혀 없기 때문이다.

 2) 인터페이스에 선언되는 건 모두 상수이거나 추상 메서드이기 때문에 public, static, final, abstract 제어자를 생략하고 쓸 수 있다.

3. 인터페이스만 조상으로 할 수 있다.

  - Object 클래스가 최고 조상이 아님

4. 다중 상속이 가능하다.

어떤 인터페이스가 2개의 인터페이스를 다중 상속을 받았다고 가정했을 때,

  - 선언부가 다르면 둘 다 상속을 받음

  - 선언부가 같은데 구현부가 다르면 어느 쪽을 상속받을지 결정할 수 없다 -> 그래서 자바는 기본적으로 단일 상속

  - 인터페이스는 추상 메서드의 집합이다.

  - 추상 메서드는 구현부가 존재하지 않음 -> 충돌 문제가 생기지 않기 때문에 다중 상속이 가능

interface Movable{ // 멤버 1개
    void move(int x, int y);
}

interface Attackable{ // 멤버 1개
    void attak(Unit u);
}

interface Fightable extends Movable, Attackable{ /* 내용 생략 */ } // 멤버 2개

 

[인터페이스의 구현] = 인터페이스에 정의된 추상 메서드를 완성하는 것

1. implements 키워드를 이용해 인터페이스를 구현할 수 있다.

// 기본 사용 방법
class 클래스이름 implements 인터페이스이름{
    // 인터페이스에 정의된 추상 메서드를 모두 구현
}

// 예시
class Fighter implements Fightable{
    public void move(int x, int y) { /* 생략 */ }
    public void attack(Unit u) { /* 생략 */ }
}

2. 인터페이스의 일부만 구현하는 경우, 클래스 앞에 abstract를 붙여야 한다.

abstract class Fighter implements Fightable{
    public void move(int x, int y) { /* 생략 */ }
    // public abstract void attack(Unit u);
}

3. 인터페이스를 구현함과 동시에 다른 클래스를 상속받을 수도 있다.

abstract class Unit {
    int x, y;
    abstract void move(int x, int y);
    void stop() { System.out.println("멈춥니다."); }
}

interface Fightable{
    void move(int x, int y); // public abstract가 생략됨
    void attack(Fightable f); // public abstract가 생략됨
}

class Fighter extends Unit implements Fightable{
    // 오버라이딩 규칙: 조상의 접근제어자보다 범위가 좁으면 안된다.
    public void move(int x, int y) { System.out.println("("+ x + ", " + y + ") (으)로 이동!"); }
    public void attack(Fightable f) { System.out.println(f+" 공격!"); }    
}

 1) 인터페이스는 부모라고 표현하긴 그렇지만, 편의상 부모라고 부른다.

 2) Unit 클래스와 Fightable 인터페이스에 같은 이름의 메서드가 있을 경우 충돌은 일어나지 않는다. -> 인터페이스에는 구현부가 없기 때문이다.

4. 인터페이스에서도 다형성을 이용할 수 있다.

// 메인
Unit u = new Fighter();
Fightable f1 = new Fighter();
Fightable f2 = new Fighter();

u.move(10, 20); // (10, 20) (으)로 이동!
u.attack(f1); // 에러. Unit 클래스에는 attack()이 없음
u.stop(); // "멈춥니다."

f1.move(50, 30); // (50, 30) (으)로 이동!
f1.attack(f2); // Fighter@4926097b 공격!
f1.stop(); // 에러. Fightable 인터페이스에는 stop()이 없음

 1) 상속받은 클래스에서의 다형성처럼 참조변수의 멤버만큼만 객체에 접근이 가능하다.

  - Fighter 객체에는 멤버가 5개 -> x, y, move(), stop(), attack() 

  - 참조변수 u로 접근 가능한 멤버는 4개 -> x, y, move(), stop()

  - 참조변수 f로 접근 가능한 멤버는 2개 -> move(), attack()

 2) 인터페이스 타입 매개변수는 인터페이스를 구현한 클래스의 객체만 가능하다.

  - attack() 메서드의 매개변수의 타입이 Fightable이므로 Fightable 인터페이스를 구현한 클래스의 인스턴스만 매개변수로 사용할 수 있다.

 3) 인터페이스를 메서드의 리턴타입으로 지정할 수 있다.

  - 인터페이스를 구현한 클래스의 인스턴스를 반환한다는 뜻이다.

// Fighter 클래스 내부
Fightable getFightable(){
    Fighter f = new Fighter();
    return (Fightable)f; // 형변환 생략 가능
    // 위 두 문장은 아래 문장과 같다.
    // return (Fightable)(new Fighter());
}

  - 위의 예시의 경우, 리턴타입이 Fightable이므로 Fightable 인터페이스를 구현한 클래스의 인스턴스를 반환한다.

'Java' 카테고리의 다른 글

디폴트 메서드  (0) 2022.05.15
인터페이스의 장점  (0) 2022.05.13
추상 클래스 작성하기  (0) 2022.05.11
추상 클래스, 추상 메서드  (0) 2022.05.05
다형성의 장점  (0) 2022.05.04