Notice
Recent Posts
Recent Comments
«   2024/09   »
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
Archives
Today
Total
관리 메뉴

충분히 쌓여가는

인터페이스를 이용한 다형성 본문

Java/객체지향

인터페이스를 이용한 다형성

빌드이너프 2023. 6. 15. 16:02

다형성: 조상 타입 참조 변수로 자손 타입 객체를 다루는 것

 

인터페이스도 구현 클래스의 부모인가?

엄밀히 말하면 아니지만 부모랑 다름없는 정도(부모는 클래스만 되기 때문)

인터페이스는 몸통이 없기 때문에 다중상속의 충돌문제를 해결하면서 다중상속과 같은 효과를 낼 수 있다

class Fighter extends Unit implements Fightable {
    public void move(int x, int y) { /* 내용 생략 */}
    public void attack(Fightable f) { /* 내용 생략 */}
}

 

따라서 인터페이스를 이용한 다형성이 성립된다

[조상 타입 참조 변수(Unit, Fightable)로 자손 타입 객체(Fighter)를 다루는 것]

Unit u = new Fighter(); // 조상 클래스 Unit
Fightable f = new Fighter(); // 인터페이스 Fightable

 

대신 Fightable에 정의된 메서드만 사용가능하다

예를 들어 코드에 정의된 move()와 attack() 메서드만 사용가능

interface Fightable {
    void move(int x, int y);
    void attack(Fightable f);
}

 

Fightable의 참조변수 f에는 move와 attack 버튼 밖에 없다(사용할 수 있는 멤버가 move와 attack로 2가지만 가능)

f.move(100, 200);
f.attack(new Fighter());


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

다시 위의 코드를 보면 인터페이스 타입 매개변수는 인터페이스를 구현한 클래스의 객체만 가능하다

매개변수 타입이 인터페이스: 인터페이스를 구현한 클래스의 인스턴스만 가능하다는 뜻

attack메서드를 보면 매개변수 타입이 Fightable로 인터페이스이다

interface Fightable {
    void move(int x, int y);
    void attack(Fightable f); // Fightable 인터페이스를 구현한 클래스의 인스턴스만 가능하다
}

인터페이스를 메서드의 return 타입으로 지정할 수 있다

Fighter 클래스가 Fightable을 구현

class Fighter extends Unit implements Fightable {
    public void move(int x, int y) { /* 내용 생략 */}
    public void attack(Fightable f) { /* 내용 생략 */}
}

 

반환 타입이 인터페이스: Fightable 인터페이스를 구현한 클래스의 인스턴스를 반환

어떤 메서드의 반환 타입이 인터페이스(Fightable)라는 것은, 메서드 내에서 인터페이스를 구현한 객체(f)를 반환해야한다

원래 반환타입(Fightable)과 반환값(f)의 타입은 일치해야 하지만 반환값(f)의 타입은 Fighter이다

이게 되는 이유는 Fighter이 Fightable로 형변환 가능하기 때문이다(형변환 가능이유는 Fightable이 Fighter의 조상이기 때문)

Fightable method() { // 리턴(반환)타입: Fightable
    ...
    Fighter f = new Fighter(); // Fighter 객체가 Fightable 구현
    return f; // Fighter 객체 반환
    // return new Fighter();
}

 

그리고 메서드를 호출한 쪽(method())에서는 반환타입(Fightable)과 일치하거나 자동형변환 가능한 타입의 변수에 결과를 저장해야 한다

Fightable f = method();

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 붙이는 이유: 오버라이딩 규칙 중 조상보다 접근제어자가 좁으면 안됨
    public void move(int x, int y) {
        System.out.printf("x: %d, y: %d%n", x, y);
    }
    public void attack(Fightable f) {
        System.out.println(f + "공격");
    }
}

public class FighterTest {
    public static void main(String[] args) {
        Fighter f = new Fighter();
        f.move(100, 200);
        f.attack(new Fighter());
    }
}

x: 100, y: 200
Fighter@36d4b5c공격

 

해당 코드 main 메서드에서 f.attack(new Fighter()); 다르게 사용

public class FighterTest {
    public static void main(String[] args) {
        Fighter f = new Fighter();
        f.move(100, 200);
//        f.attack(new Fighter());
        Fighter f2 = new Fighter();
        f.attack(f2);
    }
}

 

다형성 사용

(attack는 Unit에 없기 때문에 주석처리)

public class FighterTest {
    public static void main(String[] args) {
        // Fighter f = new Fighter();
        Unit f = new Fighter(); // Unit에 attack() 없어 호출 불가능
        f.move(100, 200);
        // f.attack(new Fighter());
    }
}

 

Fightable 사용

선언된 멤버 move와 attack 만 가능

stop 불가능

public class FighterTest {
    public static void main(String[] args) {
        Fightable f = new Fighter();
        f.move(100, 200);
        f.attack(new Fighter());
        // f.stop(); // 에러
    }
}

 

비교

Unit

public class FighterTest {
    public static void main(String[] args) {
        // Fighter f = new Fighter();
        Unit u = new Fighter();
        // Fightable f = new Fighter();
        
        u.move(100, 200);
        // u.attack(new Fighter()); // 에러
        u.stop();
    }
}

 

Fightable

public class FighterTest {
    public static void main(String[] args) {
        // Fighter f = new Fighter();
        // Unit u = new Fighter();
        Fightable f = new Fighter();

        f.move(100, 200);
        f.attack(new Fighter());
        // f.stop(); // 에러
    }
}

 

Fighter

public class FighterTest {
    public static void main(String[] args) {
        Fighter f = new Fighter();
        // Unit u = new Fighter();
        // Fightable f = new Fighter();

        f.move(100, 200);
        f.attack(new Fighter());
        f.stop();
    }
}

Fighter 클래스에 getFightable() 메서드 추가

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 붙이는 이유: 오버라이딩 규칙 중 조상보다 접근제어자가 좁으면 안됨
    public void move(int x, int y) {
        System.out.printf("x: %d, y: %d%n", x, y);
    }
    public void attack(Fightable f) {
        System.out.println(f + "공격");
    }

    Fightable getFightable() {
        Fighter f = new Fighter(); // Fighter를 생성해서 반환
        return f; // return (Fightable)f; // (Fightable) 생략됨
    }
}

public class FighterTest {
    public static void main(String[] args) {
        Fighter f = new Fighter();
        Fightable f2 = f.getFightable(); // Fightable 반환타입

    }
}

 

getFightable()에서 f가 형변환이 가능하기 때문에 (Fightable)을 생략했는데

생략하지 않고 Fightable을 사용해도 된다

Fightable getFightable() {
    Fightable f = new Fighter(); // Fightable f = (Fightable)new Fighter();
    return f;
}