[OOP] 다형성에 대해서

2025. 3. 24. 16:54·Java/OOP

  다형성이란 무엇일까?

  다형성은 (Polymorphism)으로 객체지향 프로그래밍에서 같은 이름의 메서드나 연산자가 다른 기능을 수행하는 것이다.

  다형성은 개념이 매우 중요한 기술적 부분인 것 같다. 꾸준히 다시 복습해야 다형성을 완전히 이해할 수 있을 것 같다.

 

  다형성으로 다양한 형태, 여러 형태를 지닐 수 있게 되며 다형성 덕분에 하나의 객체가 다른 타입으로 사용될 수 있다.

 

 

  다형성의 핵심 이론은 크게 2가지이다.

  1. 다형적 참조

  2. 메서드 오버라이딩

 

 


 

 

1. 다형적 참조

 

  다형적 참조를 공부하기 전 상속에 대해서 간략하게 공부해야 한다. 

  다형적 참조는 말 그대로 참조를 다양하게 할 수 있는 것이다. 하나에 국한되어 있지 않다 이 말이다.

  예제 코드를 보면 다형적 참조를 이해랄 수 있다.

  

 

package polymorphism;

public class PolymorphismEx1 {
    public static void main(String[] args) {
        Parent parent = new Parent();
        Child child = new Child();
        Parent obj = new Child();
    }
}

class Parent {
    private String name = "parent";
    /*
     Parent(){
        super();
     }
     */
}

class Child extends Parent {
    private String name = "child";
    /*
    Child(){
        super();
    }
     */
}

 

 

부모 클래스 타입의 변수가 자식 객체를 참조할 수 있는 이유는 뭘까?

 

Parent obj = new Child();  을 동작시키면 메모리상 어떤 과정을 거치게 되나 먼저 알아야 한다.

 

 


 

간략한  동작

 

스택

  지역변수 obj(참조변수) 저장

  메서드 호출 시마다 새로운 스택 프레임 생성

힙

  Child 객체가 생성될 때 힙 메모리에 저장

  부모클래스의 필드와 지식 클래시의 필드 메서드 정보를 저장한다.

 

  Child 객체 생성 과정

 

     메모리에서 Child 객체를 만들기 위해 부모 객체부터 생성하게 된다.

      자바에서는 모든 클래스의 생성자는 첫 줄에 super(); 를 호출한다는 것을 인지해야 함.

      Child 생성자가 실행되면  JVM이 저절로 super(); 를 맨 앞줄에 추가한다. super()는 Parent 기본 생성자를 가리킨다.

      즉, Child 생성 시 super()로 인해 Parent 생성자를 먼저 실행하게 된다. Parent는 힙 메모리에 저장된다.

 

  

super()에 대해 더 자세히

더보기

JVM은 생성자가 없는 클래스에 자동으로 기본 생성자를 추가한다.

여기서 JVM은 생성자 맨 앞에 super();를 붙여서 생성자를 추가한다.

 

만약에 개발자가 인스턴스 변수를 초기화시키기 위해 생성자를 만들었다고 치자 

그러면 JVM은 기본 생성자를 만들지 않을 것이다.

이때 자식 클래스는 무조건 변수 초기화를 위한 부모생성자를 super로 불러줘야 한다.

 

class Parent {
    private String name = "parent";

    Parent(String name){
        this.name = name;
    }
}

class Child extends Parent {
    private String name = "child";


    Child(String name) {
        super(name);
    }
}

 

 

자식 클래스의 생성자 super(name); 이 없다면 자식클래스를 생성하지 못하고 컴파일도 안된다.

 

 

 

참고로 자식타입 참조변수 = new 부모(); 자식타입은 부모타입 객체를 가질 수 없다.

 

 

자식클래스 타입은 부모클래스를 대입할 수 없음

 


 

다운캐스팅과 업캐스팅

 

코드

더보기
package casting;

public class DownCasting {
    public static void main(String[] args) {
        Parent parent = new Child(); //다형적 참조 부모클래스 타입은 자식클래스를 대입 가능
        //Child child = new Parent()  자식클래스 타입은 부모클래스를 대입할 수 없음;

        parent.parentMethod();
        //이제 child 메서드를 호출하고 싶다면
        //parent.childMethod(); 불가능 아무리 자식 객체를 대입했어도 타입이 Parent이기 때문
        //메모리 구조적으로 계층 아래로 메서드를 찾는건 불가능 그건 오버라이딩만 가능

        //child 메서드 호출을 위한 down casting
        Child child = (Child) parent;
        child.childMethod();

        //up casting 형변환 연산자 생략 가능
        Child child1 = new Child();
        Parent parent1 = child1;
    }
}

class Parent {
    private String name = "부모";

    Parent() {

    }

    public void parentMethod() {
        System.out.println("부모 메서드");
    }
}

class Child extends Parent {
    private String name = "자식";

    //해당 생성자 없어도 super() 포함하여 JVM이 알아서 생성
    Child() {
        super();
    }

    public void childMethod() {
        System.out.println("자식 메서드");
    }
}

 

 

 

 

 

다운 캐스팅: 부모타입이 자식객체로 다형적 참조 후 부모타입이 자식타입으로 형변환할 때 

                    다운 캐스팅은 형변환 연산자 필수

 

업 캐스팅: 자식타입이 참조변수가 부모타입으로 대입될 때

 

다운캐스팅전 Parent parent = new Child()를 하면 자식객체니 깐 자식 메서드를 호출할 수 있는 거 아닌가 싶다.

 

하지만 불가능하다. Parent 타입이므로 Parent 필드를 우선 접근한다. parent 멤버에는 child  method는 존재하지 않는다. 자바에서는 존재하지 않을 때 상속 계층 반대로 메서드를 찾지 않는다.

 

다시 말해 Parent 타입이면 자식 필드(멤버)를 접근할 수 없다.

 

접근을 위해서는 다운 캐스팅을 통해 형변환을 해줘야 한다.

 

 

 

이런 식으로 일시적 형변환도 가능하다.

((Child)parent).childMethod();

 

 

 


 

 

그래서 다형적 참조를 왜 사용할까?

 

 

  첫 째, 유연한 코드 설계

  

  다형적 참조를 사용하면 객체의 타입이 달라도 일관된 부모 클래스를 사용하여 처리할 수 있다.

 

  예를 들어 Parent 타입의 배열에 자식 타입, 손자 타입 Parent의 계층구조 아래 있는 타입들을 다 담을 수 있다.

 

코드

더보기
package polymorphism;

public class PolymorphismEx1 {
    public static void main(String[] args) {
        Parent[] parents = {new Parent(), new Child(), new MyClass()};
    }
}

class Parent {
    private String name = "parent";

}

class Child extends Parent {
    private String name = "child";

}

class MyClass extends Child {

}

 

   둘째, 코드 재사용성 증가

 

   특정 메스드가 부모 클래스를 참조하도록 하면, 새로운 자식 클래스를 추가해도 기존 코드를 수정할 필요 없다.

 

 

 

  셋째, 유지보수성 향상

 

  다형적 참조를 활용하면 OCP(Open-Closed Principle, 개방-폐쇄 원칙)을 지켜 새로운 기능을 추가해도 클라이언트나 특정 부분은 코드를 변경하지 않아도 된다.

 

 


 

OCP?

  

  소프트웨어 구성 요소(클래스, 모듈, 함수 등)는 확장에는 열려 있어야 하고, 수정에는 닫혀있어야 한다.

 

  다시 말해 기존 코드를 수정하기 않고 새로운 기능을 추가할 수 있게 설계해야 하는 소프트웨어 설계 원칙이다.

 

 

package polymorphism;

public class PolymorphismEx1 {
    public static void main(String[] args) {
        Animal dog = new Dog();
        Animal cat = new Cat();
        dog.sound();
        cat.sound();

        //새로운 동물 새 추가
        Animal bird = new Bird();
        bird.sound();
    }
}

class Animal {
    void sound() {

    }
}

class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("멍멍");
    }
}

class Cat extends Animal {
    @Override
    void sound() {
        System.out.println("야옹");
    }
}

class Bird extends Animal {
    @Override
    void sound() {
        System.out.println("짹짹");
    }
}

 

 

  Override는 뒤에 설명하겠다.

 

  OCP 원칙을 잘 지킨 바로 위 코드는 다형적 참조와, 메서드 오버라이딩을 통해 새로운 객체 Bird가 추가되어도 코드를 추가만 할 뿐 수정하지 않아도 된다.

 

 


 

오버라이딩

 

  오버라이딩 역시 다형성에서 중요한 기술이다. 

  오버라이딩은 부모 클래스의 메서드를 자식 클래스에서 가져와 다시 재정의하여 사용하는 것이다.

  {}  구현부만 수정하고 Parameter와 메서드 타입 이름은 동일해야 한다.

 

package polymorphism;

public class PolymorphismEx1 {
    public static void main(String[] args) {
        Parent obj = new Child();
        obj.h();
    }
}
class Parent{
    void h(){
        System.out.println("h");
    }
}

class Child extends Parent{
    @Override
    void h(){
        System.out.println("안녕 난 자식");
    }
}

 

  부모 타입 참조변수이지만 생성된 객체는 자식 객체이다. 

  여기서 오버라이딩은 오버라이딩된 자식 메서드를 호출한다는 점 잘 알아야 한다.

 

 

 


 

결론

 

1. 다형적 참조는 부모클래스 타입 = 자식클래스 가능!   

2. 부모클래스는 모든 자식 클래스를 참조할 수 있다.

3. 그 이유는 자식 클래스를 생성할 때 super()로 인해 부모도 자동적으로 생성되기 때문

4. 반대로 자식클래스 = 부모클래스는 안된다. 부모클래스는 자식클래스의 정보를 모르기 때문. (다형적참조는 부모=자식)

5. 어떤 기능을 찾을 때는 계층구조 밑으로는 찾을 수 없다. 오버라이딩된 메서드만 부모 -> 자식 메서드 접근 가능

6. 다형적 참조+오버라이딩으로 OCP 원칙을 지킬 수 있음. 

 

 

 

'Java > OOP' 카테고리의 다른 글

[OOP] 캡슐화 Encapsulation  (0) 2025.01.05
[Java] 변수(지역, 인스턴스, 클래스)  (0) 2024.12.01
절차 지향 프로그래밍VS객체 지향 프로그래밍  (1) 2024.11.20
[Java] Argument로 참조값을 보내면?  (0) 2024.11.16
[Java] 기본형vs참조형  (4) 2024.11.15
'Java/OOP' 카테고리의 다른 글
  • [OOP] 캡슐화 Encapsulation
  • [Java] 변수(지역, 인스턴스, 클래스)
  • 절차 지향 프로그래밍VS객체 지향 프로그래밍
  • [Java] Argument로 참조값을 보내면?
oneH
oneH
  • oneH
    Hello WeonHyeok!
    oneH
  • 전체
    오늘
    어제
    • 분류 전체보기 (50)
      • CSS (1)
      • Javascript (5)
        • JS자료구조,알고리즘 (1)
      • Java (22)
        • OOP (9)
      • JSP (1)
      • Computer Network (2)
      • 이론 컴퓨터 (2)
      • Project (0)
      • Algorithm&Data Structure (11)
      • 데이터베이스 (3)
      • Spring,SpringBoot (1)
      • Git & GitHub (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    컴퓨터 네트워크
    JS
    SQL
    프로토콜
    object
    OOP
    스택
    선형 큐
    오블완
    선택자
    이진검색
    티스토리챌린지
    Java
    컴퓨터네트워크
    Algorithm
    OSI7계층
    폰노이만 아키텍쳐
    MySQL
    math
    자바
    컴파일
    JavaScript
    Stack
    Git
    Selector
    덱
    복사
    컴퓨터구조
    큐
    combinators
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
oneH
[OOP] 다형성에 대해서
상단으로

티스토리툴바