레퍼런스 변수

|

프리미티브 타입 변수에는 각 타입에 맞는 데이터만 대입할 수 있었다.(물론 캐스팅을 하면 다른 타입도 가능하긴 하다)

하지만 레퍼런스 타입 변수에는 다형성이 있어 여러 가지 타입의 객체를 대입할 수 있는데,(이를 서브타입이라고 한다)
체나 넣을 수 있는 것은 아니고 레퍼런스 변수의 경우에는 같은 클래스와 서브 클래스 객체를 대입할 수 있다.

예를 들어  A가 슈퍼 클래스고 B - C - D가 상속받는 서브 클래스라면 A클래스 레퍼런스에는 B - C - D 객체를 대입할 수 있는 것이다.

인터페이스도 마찬가지로 서브 인터페이스나 직접 또는 간접적으로 구현하는 클래스의 객체를 대입할 수 있다.

A라는 인터페이스를 상속하는 B라는 서브 인터페이스를 구현하는 C 클래스(간접구현)가 있고 A 인터페이스를 직접 구현하는 D 클래스가 있고 이 D클래스를 상속받는 E 클래스가 있다고 하면

A 변수에는  C,D,E 클래스의 객체를 담을 수 있다.

변수를 통해 메소드나 필드에 접근하는 법은 다른 글에서 많이 배웠으니 생략하도록 하고,(오버라이딩같은)
이런 다형성을 이용한 접근에는 제약이 있는데,만일 슈퍼 클래스에는 없고 서브 클래스에만 있는 메소드나 필드를 슈퍼 클래스 레퍼런스를 통해 접근하려 한다면 컴파일 에러가 발생한다는 점이다.(반대의 경우는 당연히 될 것이다. 슈퍼에만 있는 메소드나 필드는 어차피 상속시 서브에도 똑같이 넘겨주니까)

예를 통해 보자

 

class SuperC
{
 int a = 5;
}

class SubC extends SuperC
{
 int b = 5;
}


class example
{
 
 public static void main(String[] args)
{
 SuperC obj = new SubC();
 //System.out.println(obj.b); // 슈퍼 클래스 레퍼런스로 슈퍼클래스 내부에 없는 서브 클래스의 요소에 접근할 수 없다.

 //SubC obj2 = obj; // 슈퍼 변수 = 서브 변수는 가능하지만 서브 변수 = 슈퍼 변수는 불가능하다.(슈퍼 변수가 서브 객체를 가리키고 있어도.)
 
    SubC obj2 = (SubC)obj; // 그래서 슈퍼 타입 변수를 서브 타입으로 캐스팅 해서 넣어주어야 함.

 System.out.println(obj2.b);
  
}

}

 

 

위 예제를 보면 슈퍼 변수 = 서브 객체를 선언하고 슈퍼에는 없는 필드를 접근하려 하지만 에러가 나는 것을 확인할 수 있다.
하지만 캐스팅을 거치면 가능 -> 예제 보고 추가 보완

레퍼런스 변수의 다른 제약은 상속을 역순하는 대입은 오류가 발생한다는 점이다.
1.서브 변수에 슈퍼 객체를 넣는다거나, 2.서브 변수에 슈퍼 변수를 대입한다거나.

2. 에러의 경우는 해결법이 있는데,
자바 컴파일러는 객체가 아니라 변수의 타입만 보고 대입 가능성을 검사하기 때문에,(리터럴 글에서도 그랫듯이) 대입하려면 캐스팅 연산자가 필요하다.
예제를 보면 (SubC)로 슈퍼타입 변수를 서브 클래스 타입으로 변환하면 에러가 나지 않는 것을 확인할 수 있다.

단 여기서 주의할 것은 리터럴에서 캐스팅은 데이터의 타입을 바꾸는 것이었는데 (int a = (int)3.5532 면 부동소수점타입이 정수형으로)
레퍼런스 타입에서의 캐스트 연산은 객체의 타입을 바꾸는 것이 아니라 객체를 그 타입의 변수에 대입할 수 있다는 사실을 보증만 할 뿐이다.

 

반면 1. 에러의 경우는 캐스팅으로도 불가능하다.



class SuperC
{
 int a = 5;
}

class SubC extends SuperC
{
 int b = 5;
}


class example
{
 
 public static void main(String[] args)
{
 SuperC obj = new SubC();
 SubC obj2 = (SubC)obj; // 가능

 SuperC obj3 = new SuperC();
 SubC obj4 = (SubC)obj3; // 런타임 에러 발생
 
}

}

 

그림으로 정리하면


 

 

이렇게 된다.
분명 클래스 레퍼런스에 담을 수 있는 객체는 1.자기 자신이거나 2. 서브 클래스다.
왼쪽의 경우는 서브 변수에 서브 객체를 담는 것은 당연히 된다.
하지만 오른쪽은 서브 변수에 자신의 상위 클래스인 슈퍼 클래스를 담게 되는 것이니 가능하지 않은 것이다.
자바 컴파일러는 변수의 타입만 보기 때문에 컴파일은 가능하나(캐스팅 연산자를 썼기 때문에) 알맹이는 담을 수 없는 것이기에 런타임 에러가 발생한다.


정리하자면
1. 슈퍼 변수가 갖고 있는 서브 객체의 주소값은, 캐스팅을 거쳐 서브 변수에 넣어줄 수 있다.
2. 하지만 슈퍼 변수가 갖고 있는 슈퍼 객체의 주소값은 캐스팅을 해도 서브 변수에 넣어줄 수 없다.
3. 서브 변수가 갖고 있는 서브 객체의 주소 값은 캐스팅 없이 서브나 슈퍼 변수에 넣어줄 수 있다.


이렇게 캐스트가 가능한지 불가능한지 알아주는 연산자가 있는데, 바로 instanceof 연산자다.
이 연산자는 왼쪽에는 캐스팅을 할 객체, 오른쪽에는 캐스팅하고 싶은 타입을 적으면 된다.

class SuperC
{
 int a = 5;
}

class SubC extends SuperC
{
 int b = 5;
}


class example
{
 
 public static void main(String[] args)
{
 SuperC obj = new SuperC();
 SuperC obj2 = new SubC();

 // SubC obj3 = new SuperC(); 불가
 SubC obj4 = new SubC();

 
 System.out.println(obj instanceof SubC);
 System.out.println(obj2 instanceof SubC);
 System.out.println(obj4 instanceof SuperC);

}

}

 

3번째는 아예 불가능하니 논외로 치고,
위 예제를 컴파일 하면 슈퍼 객체를 가리키는 슈퍼 변수를 서브 변수에 캐스팅 할 수 없다는 사실을 알 수 있다.

 

신고

'ComputerEngineering > JAVA' 카테고리의 다른 글

자바에서의 파일 입출력  (0) 2013.06.12
enum - 열거형  (0) 2013.06.06
레퍼런스 변수  (1) 2013.06.05
레퍼런스 타입  (0) 2013.06.05
인터페이스  (0) 2013.06.02
자바에서의 다형성  (0) 2013.06.01
trackback 0 And comment 1