티스토리 뷰

 

1. 다형성이란?

부모-자식 상속 관계에 있는 클래스에서 상위 클래스가 동일한 메시지로 하위 클래스들을 서로 다르게 동작시키는 객체 지향 원리입니다.

다형성을 활용하면부모 클래스가 자식 클래스의 동작 방식을 알수 없어도 오버라이딩을 통해 자식 클래스를 접근할 수 있습니다. 그렇다면 어떻게  부모가 자식이 어떤 일을 하는 지 몰라도,  자식 멤버 함수를 호출시킬 수 있을 까요?

이유는 동적 바인딩 때문입니다.

동적바인딩이란, 메서드가 실행 시점에서 성격이 결정되는 바인딩인데요. 프로그램의 컴파일 시점에 부모 클래스는 자신의 멤버 함수밖에 접근할 수 없으나,실행 시점에 동적 바인딩이 일어나 부모클래스가 자식 클래스의 멤버함수를 접근하여 실행할 수 있습니다.

2. 다형성 장점

  1. 간편한 유지보수
    개발자가 여러 객체를 하나의 타입으로 관리가 가능하기 때문에 코드 관리가 편리해 유지보수가 용이합니다.
  2. 재사용성 증가
    다형성을 활용하면 객체를 재사용하기 쉬워지기 때문에 개발자의 코드 재사용성이 높아집니다.
  3. 느슨한 결합
    다형성을 활용하면 클래스간 의존성이 줄어들며 확장성이 높고 결합도가 낮아져 안전성이 높아집니다.

3. 다형성 필수 조건

  1. 상속 관계다형성을 활용하기 위해서는 필수로 부모-자식 간 클래스 상속이 이루어져야 합니다.
  2. 오버라이딩 필수 (자식 클래스에서 메소드 재정의)다형성이 보장되기 위해서는 하위 클래스 메소드가 반드시 재정의되어 있어야 합니다.
  3. 업캐스팅 (자식 클래스의 객체가 부모 클래스 타입으로 형변환 되는 것)부모 타입으로 자식클래스를 업캐스팅하여 객체를 생성해야 합니다.

4. 다형성 구현 방법

  1. 상속 클래스 구현 (부모-자식 클래스 구현)
  2. 메소드 오버라이딩
  3. 업캐스팅하여 객체 선언
  4. 부모 클래스 객체로 자식 메소드 호출

5. 다형성 예시

상속 클래스 구현 (부모-자식 클래스 구현)

  • 부모 자식간 상속 클래스를 구현합니다.
  • 부모 클래스는 Book 클래스 이며 자식 클래스는 Novel(소설) 클래스입니다.
  • 디폴트 생성자 외 인수 있는 생성자를 추가해 생성자를 중복 정의합니다.
class Book{

    public String name;
    public String publisher;
    
    Book() {
    this.name = "";
    this.publisher = "";
    }

    Book(String name, String publisher){
        this.name = name;
        this.publisher = publisher;
    }

    void print(){

        System.out.println("print : Book");
    };
}

 

메소드 오버라이딩

  • 자식 클래스인 Novel, SF  클래스에 부모 메서드를 오버라이딩하여 재정의합니다.
class Novel extends Book {
    public String name;
    public String publisher;

    Novel(String name, String publisher){
        super(name, publisher);
    }

    @Override
    void print() {

        System.out.println("print : Novel");
    }
}
class SF extends Book {
    public String name;
    public String publisher;

    SF(String name, String publisher ){

        super(name, publisher);
    }

    @Override
    void print() {

        System.out.println("print : SF");
    }
}

 

업캐스팅하여 객체 선언

  • 업캐스팅(자식 클래스 객체를 부모 클래스로 형변환)하여 아래와 같이 객체를 선언합니다.
Book b = new Novel("메타버스 소설","출판사(IT)");
Book c = new SF("메타버스", "SF출판사");

 

  • 부모 클래스 객체로 자식 메소드 호출부모 클래스 Book 으로 생성된 객체의 멤버 함수를 선언합니다.
Book b = new Novel("소설","소설출판사");
b.print();
Book c = new SF("메타버스", "SF출판사");
c.print();

 

  • 결과적으로 위와 같이 다형성을 활용하면부모 클래스로 객체를 선언했으나 실행시점에 동적 바인딩되어자식클래스의 멤버함수가 호출되는 것을 볼 수 있습니다.
print : Novel
print : SF

 

객체 타입 확인 : instanceof

  • instanceof 는 객체 타입을 확인하는 연산자로, 객체의 실제 타입을 알아보기 위한 연산자입니다.
  • 아래와 같이 "객체 instanceof 타입" 과 같이 사용합니다.
  • 객체 instanceof 타입instanceof 연산자는 해당 객체가 타입이 맞다면 true, 아니면 false를 반환합니다.
if(c instanceof SF){
    c.print(); //true
}

if(b instanceof Novel){
    b.print(); //true
}

출처:

https://life-with-coding.tistory.com/485

 

[JAVA] 자바 다형성 기본 및 활용

목차 1. 다형성이란? 2. 다형성의 이점 3. 다형성 필수 조건 4. 다형성 구현 방법 5. 예제 6. 객체 타입 확인 : instanceof 📌 1. 다형성이란? 다형성(polymorphism)이란 부모-자식 상속 관계에 있는 클래스에

life-with-coding.tistory.com

 

 

 

또 다른 예제

 

class People{
     
    public void printInfo() {
        System.out.println("나는 사람입니다.");
    }
}

People 클래스에서 printInfo를 호출하게 되면 지가 사람이라는 군요.

 

class Man extends People{}
class Woman extends People{}

Man과 Woman 클래스는 People클래스를 상속합니다.

 

public class Test {
 
    public static void main(String[] args) {
        Man man=new Man();
        Woman woman=new Woman();
         
        man.printInfo();
        System.out.println();
        woman.printInfo();
    }
}

이후 메인에서는 이 두 클래스를 객체로 만들어 printInfo를 호출합니다.

이후 실행을 하게 되면 아래의 결과가 나오게 되겠죠.

나는 사람입니다.

나는 사람입니다.

두 클래스 Man과 Woman은 People이라는 클래스를 상속받았으므로 printInfo 호출시 People의 printInfo를 호출할 수 있다는 것, 뭐 놀랍지 않군요.

이제 이것을 토대로 다형성을 세세하게 알아보도록 합시다.

 

Woman, Man은 People이다

Man은 People이다. (남자는 사람이다.)

Woman은 People이다. (여자는 사람이다.)

현실 세계에서 이 다이어그램을 말로 풀어보아도 그 의미가 맞습니다.

그 반대는 어떨까요?

People은 Man이다. (사람은 남자이다.)

People은 Woman이다. (사람은 여자이다.)

사람은 남자인가요? 아니면 사람은 여자인가요?

그렇지 않습니다. 사람은 남자인지, 여자인지 알 수가 없습니다.

그렇기 때문에 반대로 표현하면 모호해진다는 것을 알 수 있지요.

여기서 중요한 점은 Man은 People로 표현할 수 있고, Woman도 People로 표현할 수 있다는 것입니다.

이것이 다형성의 개념이 나오게 됩니다. Man과 Woman은 People이기 때문에 People이라는 자료형으로 받을 수 있습니다.

public class Test {
 
    public static void main(String[] args) {
        People people=new Man();
         
        people.printInfo();
        System.out.println();
         
        people=new Woman();
        people.printInfo();
         
    }
}

실행을 시켜서 확인해보면 위의 결과와 동일한 것을 알 수 있습니다.

Man과 Woman은 People(부모클래스)로 받을 수 있다는 점을 기억하세요!

다형성과 오버라이딩

여기서 Man과 Woman은 printInfo를 물려받았고 오버라이딩(Overriding)할 수 있다는 것을 알고 있습니다.

그래서 저는 Man과 Woman의 printInfo를 그 클래스에 맞도록 오버라이딩하고 싶습니다. Man과 Woman 클래스를 다음과 같이 수정해보도록 하지요.

class Man extends People{
    @Override
    public void printInfo() {
        super.printInfo();
        System.out.println("그리고 나는 남자입니다.");
    }
     
}
class Woman extends People{
    @Override
    public void printInfo() {
        super.printInfo();
        System.out.println("그리고 나는 여자입니다.");
    }
}

그리고 실행해본다면

나는 사람입니다.

그리고 나는 남자입니다.

나는 사람입니다.

그리고 나는 여자입니다.

오버라이딩된 printInfo를 호출한다는 것을 알 수 있습니다. 오...

다형성에서 People은 자식클래스에서 재정의된 메소드를 호출할 수 있다는 것입니다.

그렇다면 Woman과 Man에서 단독으로 정의한 메소드는 어떻게 될까요?

Man과 Woman클래스에서 다음과 같이 메소드를 추가해보도록 합시다.

class Man extends People{
    @Override
    public void printInfo() {
        super.printInfo();
        System.out.println("그리고 나는 남자입니다.");
    }
     
    public void enlist() {
                System.out.println("내일 군대를 갑니다.");
        System.out.println("충성!");
    }
     
}
class Woman extends People{
    @Override
    public void printInfo() {
        super.printInfo();
        System.out.println("그리고 나는 여자입니다.");
    }
     
    public void makeUp() {
                System.out.println("예뻐질 거랍니다.");
        System.out.println("톡톡 촵촵!");
    }
}

그리고 people.enlist를 호출하려한다면 호출이 되지 않습니다. 왜냐하면 People 형이기 때문이죠. People 클래스는 enlist라는 메소드를 갖지 않기 때문에 호출할 수 없습니다.

이런 경우에는 데이터 형에 맞게 캐스팅해주어서 사용해야합니다.

바로 아래처럼요.

public class Test {
 
    public static void main(String[] args) {
        People people=new Man();
        people.printInfo();
        ((Man)people).enlist();
         
        System.out.println();
         
        people=new Woman();
        people.printInfo();
        ((Woman)people).makeUp();
         
    }
}

왜 그런걸까요?

People은 자신을 상속한 클래스 중에서 어떤 매소드를 만들지, 어떤 멤버 변수를 만들어낼지 미리 알아낼 수 없기 때문이죠.

때문에 그 메소드가 있는 객체로 직접 캐스팅해주어서 매소드를 사용해야합니다.

이해를 돕기 위한 그림이 아래에 있습니다. People이라는 자료형이 사용가능한 메소드는 printInfo밖에 없습니다. 그래서 Man의 printInfo메소드를 사용할 수 있습니다.

또한 new가 동적 메모리를 할당하는 역할을 하므로 Man이 실제 메모리에 잡히게 됩니다. 따라서 형변환을 Man으로 하는 것이 가능한 것이죠.

 

이와 같은 다형성은 어디에서 쓰일까요?

대표적으로 메소드에서 매개변수로 People을 상속하는 클래스를 받을때 사용할 수 있습니다.

public static void func(People people) {
    people.printInfo();
}
public static void main(String[] args) {
    Man man=new Man();
    Woman woman=new Woman();
    func(man);
     
    System.out.println();
    func(woman);
}

func의 매개변수 people은 People의 객체이기 때문에 그것을 상속하는 모든 클래스를 받아 낼 수 있어요.

그래서 Object 객체로 모든 객체를 받을 수 있는 것도 바로 이러한 다형성의 속성때문입니다.

또한 필요에 의해서는 instanceof 연산자를 사용해서 캐스팅할 수 있습니다.

public static void func(People people) {
    people.printInfo();
    if(people instanceof Man) 
        ((Man)people).enlist();
    if(people instanceof Woman)
        ((Woman)people).makeUp();
     
}
public static void main(String[] args) {
    Man man=new Man();
    Woman woman=new Woman();
    func(man);
     
    System.out.println();
    func(woman);
}

 

출처: https://reakwon.tistory.com/48

반응형
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
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
글 보관함