한화시스템/백엔드
[BE] JAVA_다형성
jjam-mo
2024. 7. 17. 19:36
1. 다형성의 정의 및 사용 이유
1-1. 다형성(Polymorphism)이란?
→ 타입 은닉
💡 하나의 인스턴스가 여러 가지 타입을 가질 수 있는 것을 의미한다.
하나의 타입으로 여러 타입의 인스턴스를 처리할 수 있기도 하고, 하나의 메소드 호출로 객체별로 각기 다른 방법으로 동작하게 할 수도 있다.
1-2. 다형성의 장점
- 유지보수성과 생산성 증가(여러 타입의 객체를 하나의 타입으로 관리)
- 상속관계에 있는 모든 객체는 동일한 메세지를 수신. 동잃한 메세지를 수신받아 처리하는 내용을 객체별로 다르게 할 수 있다
- 확장성이 좋은 코드 작성
- 결합도 낮춰서 유지보수성 증가
1-3. 동적 바인딩
💡 컴파일 당시에는 해당 타입의 메소드와 연결되어 있다가 런타임 시 실제 해당 인스턴스가 메소드(오버라이딩 한 메소드)로 바인딩이 바뀌어 동작하는 것
💡 동적 바인딩의 성립 조건
: 상속 관계를 가지는 부모 자식 클래스에 오버라이딩 된 메소드를 호출해야 한다.
1-4. 업캐스팅과 다운캐스팅
💡 상속관계에 있지만 오버라이딩 한 것이 아닌 후손 객체가 고유하게 가지는 확장된 기능을 사용하기 위해서는 실제 인스턴스으 ㅣ타입으로 다운캐스팅(클래스 형변환)을 해주어야 한다.
- 클래스 형변환
- 상위 타입 형변환(up-casting) : 묵시적 형변환
- 하위 타입 형변환(down-casting) : 명시적 형변환
1-5. instanceof 연산자
💡 클래스 형변환의 경우 런타임시 존재하는 타입과 형변환하려는 타입이 일치하지 않는 경우 ClassCastException이 발생.
런타임 시 안전한 형변환을 하기 위해 instanceof 연산자 이용.
instanceof 연산자는 레퍼런스 변수가 실제로 어떤 클래스 타입의 인스턴스인지 확인하여 true or false를 반환
< 전체 예시 >
package com.ohgiraffers.section01.polymorphism;
public class Application1 {
public static void main(String[] args) {
/* 수업목표. 다형성과 타입 형변환에 대해 이해할 수 있다. */
Animal animal = new Animal();
animal.eat();
animal.run();
animal.cry();
System.out.println();
Tiger tiger = new Tiger();
tiger.eat();
tiger.run();
tiger.cry();
tiger.bite();
System.out.println();
Rabbit rabbit = new Rabbit();
rabbit.eat();
rabbit.run();
rabbit.cry();
rabbit.jump();
/* 설명. 다형성 적용(부모 타입으로 자식 인스턴스의 주소값 저장) */
Animal an1 = new Animal(); // 다형성 X
Animal an2 = new Tiger(); // 다형성 O, 자동형변환
// 순서 안헷갈리게 이해하자!
Animal an3 = new Rabbit(); // 다형성 O
// 다른 타입의 객체를 배열로 다룰 수 있겠구나!! (영상 다시 봐야할듯ㅜㅜ)
/* 설명. Animal은 Tiger나 Rabbit이 아니다.(Animal Tiger나 Rabbit 타입을 지니고 있지 않다. */
// Tiger tiger1 = new Animal(); // 다형성 X
/* 설명. 동적바인딩 확인하기 */
/* 필기.
* 컴파일 당시에는 해당 타입의 메소드와 연결되어 있다가
* 런타임 당시 실제 객체가 가진 오버라이딩 된 메소드로 바인딩이 배뀌어 동작하는 것을 의미한다.
* (동적 바인딩의 조건: 1. 상속, 2. 오버라이딩)
* */
System.out.println("==== 동적 바인딩 확인하기 ====");
an1.cry();
an2.cry();
// an2.bite(); // 컴파일 시점에서 an2는 아직 객체가 생성이 안되서 해당 메소드 오류
an3.cry();
/* 설명. 부모타입을 자식 타입으로 강제 형변환 하는 것은 가능하다.(조심해야 한다.) */
// ((Tiger)an3).cry(); // 컴파일 시점에 에러가 발샏하지 않고 런타임 시점에야 에러가 발생한다.
/* 설명. 오버라이딩 되지 않은(추가한 메소드) 메소드 호출은 다운캐스팅을 해 주어야 한다. */
/* 필기.
* instanceof란?
* 해당 객체의 타입을 런타임 시점에 확인하기 위한 연산자
* */
if (an3 instanceof Tiger) { // 실행 안함!
((Tiger)an3).bite();
}
if (an3 instanceof Rabbit) {
((Rabbit)an3).jump(); // 한 번 확인하고 써야한다. 강제형변환으로 가능해진다.
System.out.println("an3는 토끼지");
}
if (an3 instanceof Animal) {
System.out.println("Animal은 맞지");
}
/* 설명. 다형성은 상속관계의 객체들간에 형변환 */
Animal animal2 = new Tiger(); // 다형성을 적용, 자동형변환(auto up-casting), 묵시적 형변화
Rabbit rabbit2 = (Rabbit)an3; // 다형성이 적용되지 않음, 강제형변환(down-casting), 명시적 형변환
}
}
// 실행 결과
동물이 먹이를 먹습니다.
동물이 달려갑니다.
동물이 울음소리를 냅니다.
호랑이가 고기를 뜯어 먹습니다.
호랑이는 왠만해선 달리지 않습니다. 어슬렁~ 어슬렁~ 걸어갑니다.
호랑이가 울부짖습니다. 어흥!~~~~
호랑이가 물어뜯습니다. 앙!~
토끼가 풀을 뜯어 먹고 있습니다.
토끼가 달려갑니다. 깡충~ 깡충~
토끼가 울음소리를 냅니다. 끼익~! 끼익~!
토끼가 점프합니다. 점프!!
==== 동적 바인딩 확인하기 ====
동물이 울음소리를 냅니다.
호랑이가 울부짖습니다. 어흥!~~~~
토끼가 울음소리를 냅니다. 끼익~! 끼익~!
토끼가 점프합니다. 점프!!
an3는 토끼지
Animal은 맞지
package com.ohgiraffers.section01.polymorphism;
public class Application2 {
public static void main(String[] args) {
/* 수업목표. 다형성을 적용하여 객체 배열을 만들어 다양한 인스턴스들을 연속 처리할 수 있다. */
Animal[] animals = new Animal[5];
animals[0] = new Rabbit();
animals[1] = new Tiger();
animals[2] = new Rabbit();
animals[3] = new Tiger();
animals[4] = new Rabbit();
// for (int i = 0; i < animals.length; i++) {
// animals[i].cry(); // cry 사용가능? 오버라이딩 되어있다.
// }
for(Animal animal : animals) {
animal.cry();
/* 설명. 오버라이딩 되지 않은 메소드는 추가 처리가 필요 */
if(animal instanceof Tiger) { // 런타임 에러 방지 코드
((Tiger)animal).bite();
}
if(animal instanceof Rabbit) {
((Rabbit)animal).jump();
}
}
}
}
// 실행 결과
토끼가 울음소리를 냅니다. 끼익~! 끼익~!
토끼가 점프합니다. 점프!!
호랑이가 울부짖습니다. 어흥!~~~~
호랑이가 물어뜯습니다. 앙!~
토끼가 울음소리를 냅니다. 끼익~! 끼익~!
토끼가 점프합니다. 점프!!
호랑이가 울부짖습니다. 어흥!~~~~
호랑이가 물어뜯습니다. 앙!~
토끼가 울음소리를 냅니다. 끼익~! 끼익~!
토끼가 점프합니다. 점프!!
package com.ohgiraffers.section01.polymorphism;
public class Animal {
public void eat() {
System.out.println("동물이 먹이를 먹습니다.");
}
public void run() {
System.out.println("동물이 달려갑니다.");
}
public void cry() {
System.out.println("동물이 울음소리를 냅니다.");
}
}
package com.ohgiraffers.section01.polymorphism;
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("호랑이가 물어뜯습니다. 앙!~");
}
}
package com.ohgiraffers.section01.polymorphism;
public class Rabbit 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 jump() {
System.out.println("토끼가 점프합니다. 점프!!");
}
}