[자바JAVA]클래스 : 다형성(Polymorphism)

캐스팅은 OOP(객체지향프로그래밍) 에서 매우 중요하다.
왜냐하면 캐스팅은 OOP의 다형성과 관련이 있기 때문이다.

다형성

java에서 기본형이 아닌 참조형데이터일때, 그리고 상속관계에 있을 때에 한정해서 슈퍼클래스 타입으로 서브클래스 객체를 레퍼런스할 수 있는 성질.

  • 동일한 형태의 구성으로 다양한 결과를 사용하는 것
  • 인터페이스에서 다형성 사용하는 이유 :
    • 하위클래스들 작성시 동일한 구조를 강요하기위해서(=통일성) 사용하기 위해서
    • request,response 정보를 사용하기 위해서




캐스팅종류(casting, 형변환)

캐스팅은 크게 자동 형변환, 명시적 형변환으로 나뉜다.
또 데이터 타입에 따라 다르다.

데이터타입 캐스팅종류
기본형 자동 형변환
기본형 명시적 형변환
(상속관계의) 참조형 자동 형변환(UpCasing)
(상속관계의) 참조형 명시적 형변환(DownCasing)

상속관계일때 캐스팅은 크게 두가지로 나뉜다.
두 캐스팅의 전제조건은 상속이다.

참고링크 : JAVA - UpCasting(업캐스팅)




UpCasting(업캐스팅 = 자동형변환) : 서브클래스가 슈퍼클래스로 형변환 되는 것.

  • 슈퍼클래스의 레퍼런스에 서브클래스의 인스턴스(객체)를 저장.
  • 컴파일러가 자동으로 형변환
  • 업캐스팅을 하면 상속 받은 멤버(멤머변수,메서드)만 쓸 수 있다.
  • 업캐스팅을 참조영역이 감소했다고 표현한다 WHY? 원래 Child는 Parent와 Child을 둘 다 쓸 수 있었는데 업캐스팅때문에 상속받은 것만 쓸 수 있게 됨
  • 업캐스팅하는 3가지방법
1
2
3
4
5
6
Parent p = new Parent();
Child c = new Child();

Parent p1 = (Parent) new Child(); //업캐스팅 1번방법
Parent p2 = new Child(); //업캐스팅 2번방법
Parent p3 = c; //업캐스팅 3번방법




DownCasting(다운캐스팅 = 명시적 형변환) : 슈퍼클래스가 서브클래스로 형변환 되는 것.

  • 다운캐스팅은 컴파일러가 자동으로 형변환 X => WHY? 해당객체가 존재하지 않을 수도 있기때문에
  • 따라서 개발자가 직접 타입을 구현해야한다. => 예외가 발생한다고 표현함.
  • UpCasting이 일어난 값에 한해서만 DownCasting이 가능하다.
  • 서브클래스의 레퍼런스에 슈퍼클래스의 인스턴스(객체)를 저장.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Parent p = new Parent();
Child c = new Child();

//아래 코드 네줄은 컴파일 에러가 발생한다. = Error
//Type mismatch: cannot convert from Parent to Child
Child c1 = new Parent();
c1.슈퍼클래스메서드명();
c1.서브클래스메서드명();
Child c2 = p;

//아래 코드 네줄은 예외가 발생한다. = Exception
//Exception in thread "main" java.lang.ClassCastException
//업캐스팅을 안해서 다운캐스팅을 할수없다 heap메모리에는 은닉된 Childrk c2가 없음.
Child c2 = (Child) new Parent();
c1.슈퍼클래스메서드명();
c1.서브클래스메서드명();
Child c2 = p;

//업캐스팅한 뒤 다운캐스팅이 가능하다
Parent pp = new Child(); //업캐스팅
Child c3 = pp;




다형성이 왜 필요할까?

  1. 다형성과 참조형 객체의 형변환
  2. insteadof 연산자
  3. 참조변수의 레벨에 따른 객체멤버 연결
  4. 공변 리턴 타입




다형성과 참조형 객체의 형변환

자바의 데이터타입은 크게 두가지로 나뉜다.

  1. 기본형데이터(8가지) : 기본 데이터형에서의 캐스팅은 원칙적으로 데이터손실을 막고자 한다.
    • 종류 : 자동 형변환, 명시적 형변환.
      아래는 double형을 int형으로 형변환한 코드이다.
1
int a = (int)1.0;
  1. 참조형데이터
    • 종류 : 자동 형변환, 명시적 형변환.
    • 조건 : 상속관계일때 성립.
      아래 예시를 보자.
      슈퍼클래스인 Parent와 서브클래스 Child가 있다.
      서브클래스 Child의 데이터양은 슈퍼클래스인 Parent보다 항상 많다.
      서브클래스가 슈퍼클래스를 포함하기때문이다.

기본적으로 같은 타입을 한 배열에 넣을 수 있다.

1
2
3
4
5
Person[] p = {new Person, new Person};

SpiderMan[] sp = {new SpiderMan, new SpiderMan};

AquaMan[] aq = {new AquaMan, new AquaMan};

하지만 spiderMan과 AquaMan은 person에 포함된다고 볼 수 있다.

  • 타입이 다른 참조형데이터를 하나의 배열에 넣을 순 없을까?
    할 수 있다!
    다형성을 적용하여 형변환을 하면 된다.
    상속관계인 경우에만 타입이 다른 참조형데이터를 하나의 배열에 묶을 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//배열선언 2가지 방법 : 1번과2번은 동일한 값을 가진다
//1번째방법 : 업캐스팅
Person[] p = new Person[3]
p[0] = new Person();
p[1] = new SpiderMan();
p[2] = new AquaMan();

//다운캐스팅
Person pp = (Person) p[0];
SpiderMan sp = (SpiderMan) p[1];
AquaMan ap = (AquaMan) p[2];


//2번째방법 : 업캐스팅 형변환
Person[] p = {new Person. new SpiderMan, new AquaMan}




instanceof 연산자

슈퍼타입을 언제나 형변환 연산자를 통해 서브타입으로 변경할 수는 없다.
먼저 메모리에 있는 객체가 형변환을 위한 충분조건을 갖추고 있는지 확인해봐야한다.
이때 사용할 수 있는 연산자가 instansof이다.

  • 실제 메모리에 있는 특정 클래스 타입인지를 boolean타입으로 리턴한다.
    따라서 결과가 true일때 형변환 처리하면 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Person {}
class SpiderMan extends Person {}

Person p = new Person();
if( p instanceof SpiderMan) //=> 변수p가 SpiderMan타입인가? 거짓
if( p instanceof Person) => 참
if( p instanceof Object) => 참

Person p2 = new SpiderMan();
if( p2 instanceof SpiderMan) // => 변수p2가 SpiderMan타입인가? 참
if( p2 instanceof Person) => 참
if( p2 instanceof Object) => 참

SpiderMan s = new SpiderMan();
if( s instanceof SpiderMan) // => 변수s가 SpiderMan타입인가? 참
if( s instanceof Person) => 참
if( s instanceof Object) => 참




참조변수의 레벨에 따른 객체멤버 연결

상속을 이용하면 조상의 멤버들을 그대로 물려받으며 필요할 때 자손은 그 멤버들을 재정의할 수 있다.
아래 코드를 보고 답변해보자

  • SubClass의 멤버변수는 몇개인가?
  • SubClass의 메서드는 몇개인가?
  • main메서드 실행 값은 순서대로 어떻게 될 것인가?
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
31
32
class SuperClass{
String x = "super"; //멤버변수

public void method(){
System.out.println("super클래스메서드");
}
}

class SubClass extends SuperClass{
String x ="sub"; //멤버변수

public void method(){ //메서드이름이 슈퍼클래스와 동일 = 메서드오버라이딩
System.out.println("sub클래스메서드");
}
}

public class SuperSubTest {
public static void main(String[] args) {
SubClass sub = new SubClass(); //객체생성
System.out.println(sub.x);
sub.method();

SuperClass super1 = sub; //업캐스팅
System.out.println(super1.x);
super1.method();
}
}
//출력값
sub
sub클래스메서드
super
sub클래스메서드
  • SubClass의 멤버변수는 2개
  • SubClass의 메서드는 오버라이딩되었기때문에 메서드는 1개다. 애노테이션(Annotation)이 있으면 구분이 쉬우니 꼭 적자. => 애노테이션위치는 subClass의 멤버변수와 메서드 사이에 위치!
  • 다이나믹바인딩때문에 super클래스의 메서드가 아니라 sub클래스의 메서드이다. 그래서 sub에서 재정의된 메서드가 실행된다.




공변 리턴 타입

원래 메서드 오버라이딩시 서브메서드의 리턴 타입은 슈퍼클래스의 리턴 타입과 같아야 한다.
하지만 공변 리턴타입는 슈퍼클래스의 리턴 타입을 서브 클래스 범위 안에서 다양하게 사용할 수 있다.
리턴타입이 변화되기때문에 오버라이딩 규칙에 위배되지만 JDK 1.5버전부터는 예외로 허용하고있다.

  • 주의점 : 상속관계상의 서브클래스에서만 가능.
  • 장점 : 형변환의 번거로움을 많이 줄일 수 있다.

참고링크 : Covariant return types