passion and relax

[JAVA] 08. 심각한 다형성 (추상 클래스와 인터페이스) 본문

프로그래밍

[JAVA] 08. 심각한 다형성 (추상 클래스와 인터페이스)

Grab Java 2024. 5. 21. 15:21

다형성

. 융통성과 확장성을 얻을 수 있다.
. 다형성을 제대로 쓰려면, interface가 필요하다.
. interface : 추상 클래스로서, 인스턴스를 만들 수 없는 클래스

 


추상 클래스

    Hippo hippo = new Hippo();       //OK
    Animal hippo = new Hippo();      //OK
    Animal animal = new Animal();    //OK이지만, Animal이 무슨 동물?

 

 

. 이런 경우 때문에, Animal 클래스는 abstract로 만들어야 한다.
. 클래스 중에 인스턴스를 만들면 안되는 경우, abstract로 만들어야 한다.
. 즉, new 키워드를 쓸 수 없게 만든다.

 

abstract public class Canine extends Animal {
    public void roam() { ~~ }
}

Canine c = new Dog();    //OK. 하위클래스의 레퍼런스를 담을 수 있다.
Canine c = new Canine(); //X. new 자체를 쓸 수 없다.

 

. 확장하지 않으면, 추상 클래스는 아무 의미가 없다. (static 멤버 제외..)
. 실제 실행되는 것은 추상 클래스의 하위 클래스의 인스턴스이다.
. 반드시 extends 해야 하는 클래스

 


추상 메소드

. 반드시 override 해야 하는 클래스.
. 몸통이 없으니, override 해야할 수 밖에..
. public abstract void eat();
. 하나라도 추상 메소드가 있다면, 그 클래스는 추상 클래스여야 한다.
. 하위 클래스를 위한 규약의 일부를 정의할 때 사용한다.

 


추상 클래스의 상속

. 추상 > 구상 : 구상에서 추상 메소드 구현
. 추상1 > 추상2 > 구상
=> 추상1은 추상2 또는 구상에서 모두 구현해야 함.
=> 추상2는 구상에서 모두 구현해야 함.

Object 클래스

Class getClass() : 그 객체의 실제 클래스 유형 리턴 (class Cat)
boolean equals() : 한 객체가 다른 객체와 같은지 여부를 리턴
int hashCode() : 객체를 hash table에 넣을 때, 필요한 객체의 hash code 리턴 (8202111)
String toString() : 객체의 내용을 String으로 반환 (cat@7d277f)

 


Object 유형의 다형적 레퍼런스 사용

. 고유의 성질을 잃는다.
. instanceOf와 cast를 이용해 고유의 성질을 되찾아 줘야 한다.
Object o = getObject(index);
if (o instanceOf Dog) {
    Dog d =  (Dog) o;
}

 


interface 상속 트리 예

Animal > Feline(고양이과) > Cat  <= implements Pet
Animal > Feline(고양이과) > Lion
Animal > Hippo
Animal > Canine(개과) > Wolf
Animal > Canine(개과) > Dog   <= implements Pet
Robot > RobotDog    <= implements Pet

 

//interface는 자동 public, abstract 이므로 안써도 된다. 여긴 썻지만
public interface Pet {      
    public abstract void beFriendly();    //interface 안의 메소드는 무조건 추상
    public abstract void play();    //interface 안의 메소드는 무조건 추상
}


public class Dog extends Canine implements Pet {
    //Pet의 추상 메소드 구현
}

 

 

. Pet의 성질을 Animal에 넣지 않고, interface로 따로 정의한다.
=> 왜? Lion, Hippo, Wolf는 Pet의 성질이 없기 때문이다.

. Pet의 성질을 Animal에 추상 메소드로 들지 않고, interface로 따로 정의한다.
=> 왜? Lion, Hippo, Wolf 는 불필요하게 빈 뼈대를 만들어야 한다.
=> 왜? 아무 작동하지 않는, 메서드가 존재하는 것은 불합리하다.

. Cat과 Dog에만 Pet의 기능을 넣지 않고, interface로 따로 정의한다.
=> 왜? 개발자가 beFriendly(), play()의 기능을 같은 이름으로 다 구현하지 않을 수도 있다.
=> 왜? 메서드명/매개변수/리턴값 등이 다를 수 있다.

 


한 클래스에서 여러 interface 구현하기

. 내가 만든 이 클래스가 저장을 해야 한다면, Serializable 인터페이스 구현.
. 내가 만든 이 클래스가 멀티 스래드를 해야 한다면, Runable 인터페이스 구현.

 

public class Dog extends Animal implements Pet, Serializable {
    ...
}

 


그냥 클래스 VS 상위/하위 클래스 VS 추상 클래스 VS 인터페이스

. 그냥 클래스
    새로운 클래스가 다른 어떤 클래스에도 'A는 B다" 의 관계가 없을 때..
상위/하위 클래스
    'A는 B다' 가 성립
    더 구체화가 되어야 할 때..
    오버라이드 하거나 새로운 메서드를 추가해야 할 때..
추상 클래스
    하위 클래스에서 사용할 틀을 정의해야 할 때..
    모든 하위 클래스에서 구현할 메소드가 있을 때..
    이 클래스의 객체를 만들지 못하게 해야할 때..
인터페이스
    상속과 무관하게 어떤 클래스들에서 여러번 사용될 역할을 정의하고 싶을 때..

 


정리

. 클래스를 만들 때, 인스턴스를 만들 수 없게 하고 싶다면 abstract 사용.
. 추상 클래스에는 추상 메소드와 일반 메소드를 넣을 수 있다.
. 추상 메소드가 하나라도 있다면, 그 클래스는 추상 클래스가 되어야 한다.
. 추상 메소드는 몸체가 없으며, 선언 부분이 세미콜론으로 끝난다. 중괄호도 없다.
. 상속 트리에서 처음 나오는 구상 클래스는 모든 추상 메소드를 구현해야 한다.
. 자바의 모든 클래스는 모두 Object의 하위 클래스이다.
. 메소드를 선언할 때, 인자, 리턴 유형을 Object 로 지정해도 된다.
. 오버라이드할 때, 상위를 확장하려 한다면, super.haha()를 먼저 호출한다.