대충은 알 것 같은 게 변수가 가지고 있는 주소로 찾아갔을 때 진짜 온전히 주소 값만 알면 그 주소로 갔을 때 어떤 데이터를 어떻게 가지고 와야 할 지 모를 것 이다.
쉬운 예로 기본 자료형 타입은 참조변수가 가리키는 주소에 찾아가서 선언된 타입만큼씩 데이터를 읽어오면 된다.
ex) int[] arr = new int[3]{1, 2, 3};
arr에 heap 메모리 주소 100이 저장되어 있다 한다면 arr[0]은 주소 100으로 가서 4byte만큼의 크기를 읽고, arr[1]은 100에서 4byte 떨어진 곳에서 4byte를 읽으면 된다. 그렇게 가지고 온 데이터를 int 타입으로 읽으면 된다.
그렇다면 배열 말고 다른 클래스 참조 변수는 어떤 식으로 heap 영역의 데이터에 접근하게 될까?
이 질문이 중요한 이유
이 답을 알게 되면 상속에서 배우는 캐스팅에 대해 더 잘 이해할 수 있게 된다.
변수의 데이터 타입에 따라 사용 할 수 있는 필드, 메서드가 달라진다. 예를 들어 A라는 클래스가 있고 A클래스를 상속받는 B클래스가 있다고 해보자.
A a = new B();
-객체지향 프로그램에서 말하는 다형성이다. B클래스로 인스턴스를 생성했지만 B클래스의 부모 클래스인 A클래스의 타입으로 변수를 선언해 주었다. 하지만 이때는 A클래스의 멤버에만 접근 할 수 있다.
B클래스의 멤버를 사용하려면 강제 캐스팅 해줘야 한다.
B b = (B)a;
이렇게 하면 B클래스 타입 b변수로 다시 B클래스의 객체 멤버들을 사용할 수 있다.
밑에는 캐스팅과 데이터 타입에 관해 조금 더 생각해 볼 수 있는 코드다.
특히 주목 할 점은 변수를 처음 선언해 줄 때 데이터 타입을 정해주는데 그 변수의 데이터 타입은 절대 바꿀 수 없다. 즉 캐스팅이 필요할 경우 새로운 변수를 만들어줘야 한다.
class A{
int a = 1;
}
class B extends A{
int b = 2;
}
public class Test{
public static void main(String[] args) {
A a1 = new B();
System.out.println(a1.a);
// System.out.println(a1.b);
// System.out.println((B)a1.b);
System.out.println(((B)a1).b);
if(a1 instanceof B) {
System.out.println(((B)a1).b);
}
a1 = (B)a1;
B b1 = (B)a1;
System.out.println(b1.b);
// System.out.println(a1.b);
}
}