어떠한 자료형/클래스의 변수/객체를 만든다는 것은, 좌변의 자료형이 요구하는 정보를 모두 우변이 갖추었을 때 가능한 것이다.
캐스팅은 형변환을 의미하는데 이 형변환 또한, 위에 맞춰서 생각하면, 좌변의 자료형에 맞게 우변의 형을 변형하는 것이다.
예를 들면 아래처럼,
int num = 1;
int형에 해당하는 변수를 만들고자 한다는 것이 좌변에 주어졌으면, 우변에는 int형을 만들기에 충분한 정보인 1이 주어지면 옳은 변수 생성이 되는 것이다.
아래 식을 보자.
int num1 = 2.0; // 에러 발생!
이 식의 경우, 컴파일 에러가 발생한다. 컴파일러가 알고 있는 int라는 기본형이긴 하지만, 우변의 데이터가 손실되는 것을 막기 위해 에러를 표시하는 것이다. 따라서 아래와 같이 형을 명시하여 캐스팅(강제형변환)을 하면 에러가 나지 않는다. 개발자가 그를 의도했다는 것이 분명하기 때문이다.
int num2 = (int) 2.0;
하지만,
double num1 = 1;
위 식도 빨간줄이 없는 문장인데, 그 이유는 손실 될 데이터도 없고 기본형끼리의 캐스팅이기 때문에 JVM에서 알아서 처리해주기 때문이다. 원래 이렇게 우변에 정보가 불충분한 경우는 에러가 뜨기 마련이다.
"좌변에 필요한 정보는 우변에 충분히 있어야한다."
이 사실을 생각하면서 업캐스팅으로 넘어가보자.
업캐스팅이란, 클래스의 상속관계가 아래과 같은 상황에서
Parent
|
Child
Parent형의 객체를 생성하고자 할 때, Child형의 정보를 좌변에 제공하는 것이다.
Parent p = new Child();
상위클래스인 좌변이 요구하는 정보는 그의 하위클래스인 우변은 당연히 전부 가지고있기 때문에 위 문장은 옳은 문장이 된다.
말하자면 업캐스팅이란, 하위 클래스의 정보를 담을 수 있는 객체에 상위클래스의 자료형을 부여해서, 상위클래스처럼 사용하게 하는 것이다.
그렇다면 다운캐스팅이란 뭘까? 단순히 업캐스팅의 반대라고 생각하면 될까?
그렇지 않다.
다운캐스팅이란 하위클래스(Child)의 정보를 담을 수 있는 객체의 자료형이 상위클래스(Parent)로 전환되어 있던 것(업캐스팅된 객체)을 다시 되돌리는 것을 의미한다. 업캐스팅 되었던 객체의 자료형을 다시 하위클래스의 정보를 담는 기능을 하도록 자료형을 Child로 바꾸어서 되돌려 놓는 것을 말한다.
Parent p = new Child(); //업캐스팅 -p는 Parent형.
Child c = (Child) p; //다운캐스팅! -p는 Child형.
원칙적으로 다운캐스팅 혼자만 쓰면 우변이 좌변에서 필요한 정보를 모두 채워주지 못하기 때문에 불가능한 문장이 되고, 꼭 업캐스팅이 선행되어야 한다는 것을 명심해야한다. 아래는 에러가 나는 코드이다.
Parent p = new Parent();
Child c = (Child) p;//에러!!
OOP 공부할 때 가장 의문을 품었던 부분이다. 애초에 용도에 맞게 자료형을 객체에 지정해주고 그거 대로 쓰면되지 왜 자료형을 이리저리 바꾸는 걸까???
안드로이드 공부를 하면서 그 해답을 찾았다. 바로 아래와 같은 경우 때문이다.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.button1).setOnClickListener(this);
findViewById(R.id.button2).setOnClickListener(this);
findViewById(R.id.button3).setOnClickListener(this);
}
@Override
public void onClick(View v) {
Button button = (Button) v; // 다운캐스팅 발생!
setFragment(button.getText().toString());
}
위는 안드로이드 공부 중, 도전 프로젝트7에서 MainActivity.java의 코드의 일부분이다.
findViewById(R.id.button1).setOnClickListener(this);
이 부분에서 Button은 setOnClickListener()에 의해 onClick()메소드의 인자인 View v로 넘겨지게 된다.
즉, 이 메소드가 호출되는 과정에서
View v = findViewById(R.id.button1);
/*
findViewById()는 아이디로 뷰를 찾고 그를 자바코드에서 사용 가능한
객체로 반환해주는 메소드이므로, 이 경우 Button 자료형을 가지고,
button1이라는 id를 가진 뷰를 반환한다.
*/
위와 같이 v에 대한 업캐스팅이 발생한 것이다.
그래서, onClick()메소드 안 에서는 Button의 특성을 다시 살려서 쓰기 위해
Button button = (Button) v;
위와 같이 다운캐스팅을 해준 것이다.
안드로이드 공부에서 나오지만, 기본적으로 View라는 상위 클래스를 Button을 포함한 수많은 클래스들이 상속받기 때문에 이런 업/다운 캐스팅을 적극 활용하면 View 클래스에만 setOnClickListener(), onClick()와 같은 메소드들을 잘 정의하면 그를 상속받는 모든 하위클래스에서 이 메소드들을 오버로딩하여 활용할 수 있다.
OOP에서 말하는 다형성인 것이다!