[Java] 객체지향 - 1. 다형성, 동적바인딩, instanceof 연산자, 클래스 형변환
다형성이란?
하나의 인스턴스가 여러가지 타입을 가질 수 있는 것을 의미한다.
다형성은 상속을 기반으로 한 기술이다.
하나의 타입으로 여러 타입의 인스턴스를 처리할 수 있고,
하나의 메소드 호출로 객체별로 각기 다른 방법으로 동작하게할 수 있다.
1. Parent p = new Parent(); (O)
2. Child c = new Child(); (O)
3. Parent p2 = new Child(); (O) -> 다형성 기술이 적용되어서 가능함
4. Child c2 = new Parent(); (X) -> Child클래스가 Parent클래스를 다룰 수 없음
Parent[] arr = new parent[3];
arr[0] = new Child1();
arr[1] = new Child2();
arr[2] = new Child3();
다형성의 특징
- 여러 타입의 객체를 하나의 타입으로 관리할 수 있으므로 유지보수성과 생산성이 증가됨
- 상속 관계에 있는 모든 객체는 동일한 메소드(메시지)를 수신할 수 있음 (역할)
- 확장성이 좋은 코드 작성 가능
- 결합도를 낮춰 유지보수성을 증가시킬 수 있음
동적바인딩
- 컴파일 당시에는 해당 타입의 메소드와 연결되어 있다가 런타임 당시 실제 객체가 가진 오버라이딩된 메소드로 바인딩이 바뀌어 동작하는 것을 의미
- 동적바인딩 성립 조건 : 상속 관계로 이루어져 다형성이 적용된 경우. 메소드 오버라이딩이 되어 있어야 함
Instanceof 연산자
현재 레퍼런스 변수가 어떤 클래스 타입의 객체 주소를 참조하고 있는지를 확인할 때 사용
클래스 타입이 맞으면 true, 맞지 않으면 false를 반환
If(레퍼런스) instanceof 클래스타입) {
//true일때 처리할 내용. 해당 클래스 타입으로 down casting
}
클래스 형변환
- 업캐스팅(up-casting) : 상위(부모) 타입으로 형변환
ex) Car c = (Car)New Sonata();
- 다운캐스팅(down-casting) : 하위(자식) 타입으로 형변환
ex) ((Sonata)c).moveSonata();
- 묵시적 형변환 : up-casting의 경우 묵시적 형변환이 적용됨 (자동 형변환)
ex) Car c = new Sonata();
위의 개념들을 코드로 적용
Animal Class
public class Animal {
/* 동물은 기본적으로 먹는 행동과 뛰는 행동, 우는 행동을 할 수 있다. */
public void eat() {
System.out.println("동물이 먹이를 먹습니다.");
}
public void run() {
System.out.println("동물이 달려갑니다.");
}
public void cry() {
System.out.println("동물이 울음 소리를 냅니다.");
}
Animal 클래스에 상속된 Rabbit 클래스
public class Rabbit extends Animal{
/* 동물이 하는 행동을 조금 구체화하여 행동할 수 있도록
* Rabbit 클래스에 Animal 클래스의 메소드를 오버라이딩 한다.
* */
@Override
public void eat() {
System.out.println("토끼가 풀을 뜯어먹고 있습니다.");
}
@Override
public void run() {
System.out.println("토끼가 달려갑니다. 깡총 깡총~");
}
@Override
public void cry() {
System.out.println("토끼가 울음소리를 냅니다. 끼익 끼익~");
}
/* 추가적으로 토끼는 점프를 뛸 수 있다. */
public void jump() {
System.out.println("토끼가 점프합니다. 점프!!");
}
Animal 클래스에 상속된 Tiger 클래스
public class Tiger extends Animal{
@Override
public void eat() {
System.out.println("호랑이가 고기를 뜯어먹고 있습니다.");
}
@Override
public void run() {
System.out.println("호랑이는 웬만해서 달리지 않습니다. 어슬렁 어슬렁~ 걸어갑니다.");
}
@Override
public void cry() {
System.out.println("호랑이가 울부짖습니다. 어흐으응~!!");
}
/* 호랑이는 추가적으로 물어뜯기를 할 수 있다. */
public void bite() {
System.out.println("호랑이가 물어 뜯습니다. 앙~");
}
Application(출력) 클래스
public class Application {
public static void main(String[] args) {
System.out.println("Animal 생성 ------");
Animal animal = new Animal();
animal.eat();
animal.run();
animal.cry();
System.out.println("Rabbit 생성 ------");
Rabbit rabbit = new Rabbit();
rabbit.eat();
rabbit.run();
rabbit.cry();
rabbit.jump();
System.out.println("Tiger 생성 ------");
Tiger tiger = new Tiger();
tiger.eat();
tiger.run();
tiger.cry();
tiger.bite();
/* Rabbit은 Rabbit 타입이기도 하면서 Animal 타입이기도 함
* Tiger는 Tiger 타입이기도 하면서 Animal 타입이기도 함
* */
Animal animal1 = new Rabbit();
Animal animal2 = new Tiger();
/* 하지만 반대로 Animal은 Animal일뿐 Rabbit, Tiger가 될 수 없다.
* 그래서 반대로 작성할 시 에러 발생한다.
* */
//Rabbit r = new Animal();
//Tiger t = new Animal();
출력 클래스에서 동적 바인딩 확인
System.out.println("동적바인딩 확인 ------");
/* 컴파일 당시에는 해당 타입의 메소드와 연결되어 있다가,
* 런타임 당시 실제 객체가 가진 오버라이딩 된 메소드로 바인딩이 바뀌어 동작한다.
* */
animal1.cry();
/* 하나의 메소드 호출로 각기 다른 객체의 다른 메소드를 동작시키게 한다. */
//animal1.jump();
//animal2.bite();
위의 코드에서 현재 레퍼런스 변수 타입은 Animal 이기 때문에
Rabbit과 Tiger이 가지고 있는 고유한 기능(jump, bite)고유한 기능을 동작시키지 못한다.
이러한 문제점을 해결하기 위해 클래스타입 형변환을 확인 해보자.
System.out.println("클래스타입 형변환 확인 ------");
((Rabbit)animal1).jump();
((Tiger)animal2).bite();
/* 타입 형변환을 잘못 하는 경우
* 컴파일 시에는 문제가 되진 않지만 런타임시 Exception이 발생한다.
* */
//((Tiger)animal1).bite(); //토끼 인스턴스는 호랑이가 될 수 없다.
객체별로 고유한 기능을 동작시키기 위해서는 레퍼런스 변수를 형변환하여 Rabbit과 Tiger로 변경해야 메소드 호출이 가능하다.
타입 형변환 시 실제 인스턴스와 타입이 일치하지 않는 경우에는 ClassCastException이 발생할 수 있다.
레퍼런스 변수가 참조하는 실제 인스턴스가 원하는 타입과 맞는지 비교하는 연산자 instanceof
System.out.println("ionstanceof 확인 ------");
System.out.println("animal1이 Tiger 타입인지 확인 : " + (animal1 instanceof Tiger)); //false
System.out.println("animal1이 Rabbit 타입인지 확인 : " + (animal1 instanceof Rabbit)); //true
System.out.println("animal2이 Tiger 타입인지 확인 : " + (animal2 instanceof Tiger)); //false
System.out.println("animal2이 Rabbit 타입인지 확인 : " + (animal2 instanceof Rabbit)); //true
/* 상속 받은 타입도 함께 가지고 있다(다형성) */
System.out.println("animal1이 Animal 타입인지 확인 : " + (animal1 instanceof Animal)); //true
System.out.println("animal2이 Animal 타입인지 확인 : " + (animal2 instanceof Animal)); //true
/* 모든 클래스는 Object의 후손이다. */
System.out.println("animal1이 Object 타입인지 확인 : " + (animal1 instanceof Object)); //true
System.out.println("animal2이 Object 타입인지 확인 : " + (animal2 instanceof Object)); //true