[JAVA] 다형성(Polymorphism)

김민기·2021년 2월 12일
0

Java

목록 보기
12/20
post-thumbnail

다형성

  • 객체지향 개념에서 다형성이란 여러가지 형태를 가질 수 있는 능력을 말함
  • 타입 캐스팅 혹은 메소드의 오버로딩 및 오버라이딩으로 다형성 구현
  • 타입 캐스팅을 통한 다형성 구현은 실제 객체를 변경하는게 아니라 참조타입만 바꾸는 것

자동 형변환을 통한 타입 다형성

  • 형변환에서의 크다는 메모리의 크기가 아닌 범위를 뜻함 → Object 클래스는 모든 클래스의 슈퍼클래스 이므로 범위가 가장 큼
  • 범위가 넓은 레퍼런스 혹은 기본형 변수로 더 범위가 좁은 객체 또는 기본형 타입을 참조할 수 있다
  • 이를 up-casting이라고하며 up-casting은 명시적으로 형변환하지 않아도 자동형변환 된다.

Primitive type

  • primitive type은 double이 표현할 수 있는 범위가 가장 크고 byte가 가장 작음
  • byte → double 까지 자동형변환 되므로, 메서드 파라미터에서 double형으로 모든 primitive 타입을 받아 캐스팅하여 사용 가능
  • boolean type은 형변환이 불가능

Reference type

  • 슈퍼클래스 타입으로 서브클래스의 객체를 레퍼런싱 가능
    • Object o = new String(); → 모든 클래스는 Object 클래스를 상속하므로 가능
  • 이 때 서브클래스의 인스턴스는 업캐스팅을 통한 자동형변환(비 명시적)
  • 서브클래스 인스턴스를 참조하는 슈퍼클래스는 서브클래스의 멤버에 접근할 수 없음
    • 서브클래스의 객체에 실제로 멤버가 존재하나 슈퍼클래스에 존재하지 않는 멤버는 참조 불가능 → Shadow impact
    • 예외적으로 오버라이딩 된 메소드는 재정의된 것으로 슈퍼클래스 메소드 대신 자식 메소드를 호출
      • Dynamic Linking
      • ex) 매개변수를 object로 받고 하위 메소드로 값을 비교하는 경우가 가능
class A{}
class B extends A{ String toString(){}}
class C extends B{}

Object x = new B();
B.toString()
//B.toString()

Object x = new A();
A.toString()
//Object.toString()

B x = new Object();
B.toString();
//error 자식 레퍼런스 - 부모 객체 불가능

A x = new C();
C.toString();
//B.toString()
  • 레퍼런스의 선언된 클래스의 메소드만 호출 가능하다 → 하위 객체로 생성되어 오버라이딩으로 재정의 되었다면 하위 객체의 메소드를 호출한다

여러 상황에서 Reference Casting

  • 매개변수 자동형변환
    • 메소드를 파라미터의 타입 별로 오버로딩하는 대신 파라미터를 Object 타입으로 받아서 동작할 수 있음
  • 배열 및 컬렉션 자동형변환
    • 슈퍼클래스 타입으로 배열과 컬렉션 선언 시 서브클래스의 객체를 담을 수 있음.

슈퍼클래스와 서브클래스의 멤버 변수

  • 슈퍼클래스와 서브클래스에서 같은 이름으로 멤버변수를 선언했더라도 호출할 때는 레퍼런스 타입에 정의된 멤버 변수를 호출한다.
  • super와 this로 멤버 변수를 호출할 클래스를 정의

명시적 형변환

  • 자동 형변환이 되지 않는 다운 캐스팅
  • 실제로 참조하고 있는 객체로만 가능
    • 서브클래스가 슈퍼클래스를 참조하거나 상속관계가 아닌 클래스를 참조할 수 없음
    • 에러 : ClassCastException(런타임 에러)
    • 문법적으로 super클래스의 sub클래스의 형변환은 가능하지만 실제 sub클래스가 참조할 수 없는 메모리에 접근하므로 런타임 에러
  • Shadow Impact를 해결하기 위해 업캐스팅한 레퍼런스를 되돌려줄 때 사용
class Car{
    void run(){
        System.out.println("run");
    }
}
class PoliceCar extends car{
    @Override
    void run(){
        System.out.println("run with siren");
    }
    void siren(){
        System.out.println("siren");
    }
}
public class Main{
    public static void main(String[] args){
        Car myCar = new PoliceCar();
        PoliceCar errorCar = new Car();
        // 런타임 에러
        myCar.run();
        // run with siren 출력
        myCar.siren()
        // 컴파일 에러
        ((PoliceCar)myCar).siren();
        // siren 출력
    }
}

instanceof

  • A instanceof B A의 객체를 B가 참조할 수 있는가? (형변환이 가능한가)
    • 즉 A가 B의 인스턴스인가?
    • B가 A의 슈퍼클래스일 경우 혹은 B와 A가 같은 클래스일 경우 true
      • ex) 클래스 instanceof Object 는 항상 true

메소드 다형성

  • 오버로딩
    • 같은 클래스 내에 같은 이름을 갖는 메소드를 파라미터를 다르게 하여 별개의 메소드로 선언
    • 새로운 메소드를 만드는 것
  • 오버라이딩
    • 상속 관계에서 슈퍼클래스의 메소드를 서브클래스에서 재정의
    • 기존의 메소드를 덮어쓰는 것(재정의)
    • 파라미터와 반환값을 포함한 내용이 모두 같아야함
  • 오버라이드 어노테이션
    • @Override
    • 명시적으로 오버라이딩을 했다는 것을 작성하여, 가독성을 확보하고 컴파일 시 슈퍼클래스 메소드가 변화하더라도 컴파일러가 인지할 수 있도록 함
  • 메소드 시그니처
    • 자바 컴파일러는 메소드 시그니처( 메소드명과 파라미터)를 통해 같은 메소드인지 판단함.
    • 반환타입과 exeption은 메소드 시그니처 고려대상이 아님.
void myMethod(int a){}
int myMethod(int a) throws IOException{}
// 둘은 메소드 시그니처가 같으므로 오버로딩되었다고 판단하지 않고 중복선언된 메소드라고 판단함-> 컴파일 에러

오버라이딩 - Dynamic Linking(Virtual Invocation)

  • 다형성 관계에서 overriding 된 메소드가 호출

  • 상속 관계에서 기존의 코드를 수정하지 않고 추가된 메소드를 반영

  • static 메소드는 오버라이딩을 허용하지 않음

    • 클래스의 상속은 객체 생성 시에 이루어지므로 클래스 로딩시에 정의되는 스태틱 메소드에 오버라이딩읠 정의 할 수 없음
    • 서브클래스에 같은 스태틱 메서드를 재정의하는 것을 hiding이라고 함
  • 슈퍼클래스에 선언된 메소드가 원하는 기능에 따라 각각의 서브클래스들에서 다른 동작을 통한 같은 기능을 하도록 오버라이딩

    • ex) servlet.service() → 서브클래스에서 각각 다 다른 service 수행하고 같은 결과를 반환하게 함
  • 업캐스팅된 레퍼런스에서 오버라이딩하기 전 슈퍼클래스의 메소드에 접근할 수 있을까?

    • Dynamic Linking을 통해 자동으로 자식 객체의 메서드를 호출하게 됨.
    • 호출하기 위해서는 오버라이딩 된 메소드에서 super().메소드를 해주는 객체를 재정의해야함
    • 혹은 접근제어자로 오버라이딩 된 메소드의 접근을 제어하면 슈퍼클래스의 메소드가 호출될 수 있음
profile
민기1

1개의 댓글

comment-user-thumbnail
2021년 2월 22일

무야호

답글 달기