다형성
부모 클래스 타입 참조변수 하나로 상속 관계에 있는 여러 타입의 자식 객체를 참조할 수 있는 기술
다형성을 이용한 객체 배열
부모 타입 참조 자료형의 변수를 하나의 묶음으로 다루는 것
바인딩: 실제 실행할 메소드 코드와 호출할 코드를 연결시키는 것
-정적 바인딩: 프로그램 실행 전 컴파일 단계에서 메소드와 메소드 호출부 연결
-동적 바인딩: 컴파일 시 정적 바인딩된 메소드를 실행할 당시의 객체타입 기준으로 바인딩 되는 것
*동적바인딩 장점: 업 캐스팅 상태의 참조변수를 별도의 다운 캐스팅 없이 자식의 오버라이딩된 메소드를 수행할 수 있다.
package edu.kh.poly.ex1.model.vo;
public class Car {
private String engine; //엔진
private String fuel; //연료
private int wheel; //바퀴 개수
public Car() {//기본 생성자
super(); //부모 생성자
}
//매개변수 생성자 자동완성
//alt shiht S -> O -> enter
public Car(String engine, String fuel, int wheel) {
super();
this.engine = engine;
this.fuel = fuel;
this.wheel = wheel;
}
//getter setter 자동완성
//alt shift S -> r -> tab -> space -> shift tab -> enter
public String getEngine() {
return engine;
}
public void setEngine(String engine) {
this.engine = engine;
}
public String getFuel() {
return fuel;
}
public void setFuel(String fuel) {
this.fuel = fuel;
}
public int getWheel() {
return wheel;
}
public void setWheel(int wheel) {
this.wheel = wheel;
}
//Object.toString() 오버라이딩
@Override
public String toString() {
return engine + " / " + fuel + " / " + wheel;
}
}
package edu.kh.poly.ex1.model.vo;
public class Tesla extends Car{ //전기차
private int batteryCapacity; //배터리 용량
public Tesla() {
super(); //부모 생성자 Car
}
//매개변수 생성자
//alt shift S -> O -> 위쪽에 메뉴 바꾼 후 enter
public Tesla(String engine, String fuel, int wheel, int batteryCapacity) {
super(engine, fuel, wheel);
this.batteryCapacity = batteryCapacity;
}
//getter setter
public int getBatteryCapacity() {
return batteryCapacity;
}
public void setBatteryCapacity(int batteryCapacity) {
this.batteryCapacity = batteryCapacity;
}
//Car.toString 오버라이딩
@Override
public String toString () {
return super.toString() + " / " +batteryCapacity;
}
}
package edu.kh.poly.ex1.model.vo;
public class Spark extends Car{ //경차
private double discountOffer; //경차 할인 혜택
//기본 생성자
public Spark() {
super(); //생략 시 컴파일러가 자동 추가
}
//매개변수 생성자(상속버전)
//alt shift S -> O -> 아래방향키 -> enter
public Spark(String engine, String fuel, int wheel, double discountOffer) {
super(engine, fuel, wheel);
this.discountOffer = discountOffer;
}
//getter setter
public double getDiscountOffer() {
return discountOffer;
}
public void setDiscountOffer(double discountOffer) {
this.discountOffer = discountOffer;
}
@Override
public String toString() {
return super.toString() + " / " + discountOffer;
}
}
package edu.kh.poly.ex1.model.sevice;
import edu.kh.poly.ex1.model.vo.Car;
import edu.kh.poly.ex1.model.vo.Spark;
import edu.kh.poly.ex1.model.vo.Tesla;
public class PolyService {
public void ex1() {
//다형성 확인 예제
//Car 객체 생성
Car car = new Car();
//부모 타입 참조변수 Car로 부모 객체 참조
//Tesla 객체 생성
Tesla tesla = new Tesla();
//자식 타입 참조변수 Tesla로 자식 객체 참조
//******다형성(업캐스팅)******
Car car2 = new Tesla(); //-> 오류 발생 안 함
//Tesla 객체를 참조하는 변수의 타입이 Car(부모)이기 때문에
//Tesla 객체가 Car(부모) 객체로 변화함
Car car3 = new Spark();
//부모타입 참조변수 =(참조) 자식 객체
//******다형성(업캐스팅) 작성법******
//1) 자식 객체가 부모 객체로 변했기 때문에
// 자식만의 고유한 필드, 메소드를 사용할 수 없다.
//1-1) car(부모 = 부모)
car.setEngine("v6 6기통 엔진");
car.setFuel("휘발유");
car.setWheel(4);
// Car 메소드 모두 사용 가능
//1-2) tesla (자식 = 자식)
tesla.setEngine("전기모터");
tesla.setFuel("전기");
tesla.setWheel(4);
tesla.setBatteryCapacity(1_000_000);
// tesla 메소드 모두 사용 가능
//1-3) car2 (부모 = 자식(Tesla))
car2.setEngine("전기모터");
car2.setFuel("전기");
car2.setWheel(4);
//car2.setBatteryCapacity(1_000_000);
//The method setBatteryCapacity(int) is undefined for the type Car
//1-4) car3 (부모 = 자식(Spark))
car3.setEngine("경차엔진");
car3.setFuel("휘발유");
car3.setWheel(4);
//car3.setDiscountOffer(0.5);
//The method setDiscountOffer(double) is undefined for the type Car
//=============================================================
//2) 다형성을 이용한 객체 배열
// 객체 배열: 같은 객체 참조 자료형의 변수를 하나의 묶음으로 다루는 것
// + 다형성 적용 -> 부모 타입 참조 자료형의 변수를 하나의 묶음으로 다루는 것
Car[] arr = new Car[3]; //부모 타입 참조 변수 배열 선언 및 할당
//각 배열 요소가 Car 타입 참조 변수
arr[0] = car; //Car 주소 == Car 객체
//Car 참조 변수
arr[1] = car2; //Tesla 주소 == Tesla 객체
//Car 참조변수
arr[2] = car3; //Spark 주소 == Spark 객체
//Car 참조변수
//상속 + 다형성
//상속 특징: 일련의 클래스들에 대한 공통적인 규약 정의
// -> Car 상속 클래스는 모두 getEngine()을 가지고 있다를 정의
//다형성(업캐스팅): 부모 타입 참조 변수 arr[i]로 자식 객체를 참조할 수 있다.
for (int i = 0; i<arr.length; i++) {
System.out.println(i+ "번째 인덱스의 엔진: "+arr[i].getEngine());
}
}
public void ex2() {
//3) 다형성(업캐스팅)을 이용한 매개변수 사용법
Tesla t = new Tesla("전기모터", "전기", 4, 1000000);
Spark s = new Spark("경차 엔진", "휘발유", 4, 0.5);
Car c = new Car("경유 엔진", "경유", 12);
printCar(t);
printCar(s);
printCar(c);
System.out.println("=================================");
//4) 다형성을 이용한 반환형 사용법
// Car arr[] = {new Car(), new Tesla(), new Spark()};
Car[] arr = {createCar(1), createCar(2), createCar(3)};
//반환형 Car Car Car
//(Tesla) (Spark)
// arr[0];// Car
// arr[1];// Tesla
// arr[2];// Spark
//instanceof 연산자: 객체의 자료형을 검사하는 연산자
// -> 참조하는 객체가 특정 자료형이거나 부모쪽 상속 관계인지를 확인
//arr[1]이 참조하는 객체가 Tesla이면 true,아니면 false를 반환
System.out.println(arr[1] instanceof Tesla); //true
//arr[1]이 참조하는 객체가 Spark이면 true,아니면 false를 반환
System.out.println(arr[1] instanceof Spark); //false
//arr[1]이 참조하는 객체가 Car이면 true,아니면 false를 반환
System.out.println(arr[1] instanceof Car); //true
//Car는 Tesla의 부모쪽 상속 관계이기 때문에 true
System.out.println("==============================");
for (int i = 0; i<arr.length; i++) {
if(arr[i] instanceof Tesla) {
System.out.println("Tesla 객체입니다.");
} else if (arr[i] instanceof Spark) {
System.out.println("Spark 객체입니다.");
} else {
System.out.println("Car 객체입니다.");
}
}
//Car 조건문을 먼저 넣으면 전부 다 Car로 나옴
}
//전달받은 Car 또는 자식 객체의 엔진, 연료, 바퀴 개수를 출력하는 메소드
//매개변수에 부모타입 참조변수를 작성하면 모든 자식의 객체를 전달 받을 수 있다.
public void printCar(Car temp) { //부모타입 참조변수 =(대입) 자식 객체
//매개변수에 작성된 참조형 변수에는 주소가 저장됨(얕은 복사)
//메소드 내부 변수 + 매개변수 == 지역변수(Local Variable)
System.out.println("엔진: " + temp.getEngine());
System.out.println("연료: " + temp.getFuel());
System.out.println("바퀴 개수: " + temp.getWheel());
System.out.println();
}
//전달받은 매개변수에 따라서 Car 또는 자식 객체를 생성하고
//생성된 객체의 주소를 반환
public Car createCar(int num) {
Car result = null; //아무것도 참조하고 있지 않음
switch(num) {
case 1: result = new Car(); break;
case 2: result = new Tesla(); break;
case 3: result = new Spark(); break;
}
//반환형이 Car이지만
//case가 2, 3이면 Car의 자식 객체의 주소가 반환된다.
return result;
}
public void ex3() {
//******다형성 중 다운캐스팅******
// 다운캐스팅: 부모 타입 참조 변수가 자식 객체를 참조하는
// 업캐스팅 상태에서만 진행할 수 있는 기술로
// 부모 타입을 자식 타입으로 "강제 형변환"해서
// 자식 객체의 본래 필드, 메소드를 사용 가능하게 한다.
Car c1 = new Tesla("전기모터", "전기", 4, 50000);
System.out.println(((Tesla)c1).getBatteryCapacity()); //50000
//주의!!! "." 연산자가 (Tesla)형변환 연산자보다 우선순위가 높음
//-> 괄호로 묶어서 우선순위 재배정
//<효율적인 다운캐스팅 방법>
// -얕은 복사 이용
Tesla t1 = (Tesla)c1;
System.out.println(t1.getBatteryCapacity()); //50000
}
public void ex4() {
//!!!!!!다운캐스팅 주의 사항!!!!!!
Car c1 = new Tesla();
// Spark s1 = (Spark) c1; //다운캐스팅
//java.lang.ClassCastException (형변환 예외)
//-> c1이 참조하는 객체는 Tesla인데
// Spark 참조변수로 Tesla를 참조하려 해서 문제 발생
//해결방법: instanceof와 같이 사용
if (c1 instanceof Spark) {
Spark s1 = (Spark) c1; //다운 캐스팅
System.out.println("성공");
}else {
System.out.println("실패(Spark 타입 아님)");
}
}
public void ex5() {
// 바인딩(Binding)
// - 실제 실행할 메소드 코드와 호출하는 코드를 연결시키는 것
Car c1 = new Car("경유 엔진", "경유", 8);
System.out.println(c1.getEngine());
//Car 객체에 있는 getEngine() 메소드 호출 == 바인딩
//String edu.kh.poly.ex1.model.vo.Car.getEngine()
//프로그램 "실행 전"
//-컴파일러는 getEngine() 메소드가 Car에 있는 걸로 인식해서
// c1.getEngine() 호출 코드와
// String edu.kh.poly.ex1.model.vo.Car.getEngine() 메소드 코드를 연결해줌
//-> [정적 바인딩]
// 컴파일 단계에서 메소드와 메소드 호출부를 연결
System.out.println(c1.toString());
//String edu.kh.poly.ex1.model.vo.Car.toString()
//Car 참조변수 c1을 이용해서
//Car 객체에 있는 오버라이딩된 toString() 메소드 호출
//**다형성 적용 시 바인딩**
Car c2 = new Spark("경차엔진", "휘발유", 4, 0.5);
//업캐스팅 -> 부모 부분만 참조 가능한 상태
System.out.println(c2.toString());
//String edu.kh.poly.ex1.model.vo.Car.toString()
//참조변수 c2가 Car 타입이므로
//toString() 도 Car의 toString()을 호출 - 정적 바인딩
//하지만 실행해보면 자식(Spark)의 toString()이 호출되는 것을 확인 가능
//-> 컴파일 시에는 부모(Car)와 바인딩 ==[정적 바인딩]
//-> 실행 시에는 자식(Spark)에 오버라이딩된 메소드와 바인딩 == [동적 바인딩]
//컴파일 시 정적 바인딩된 메소드를 실행할 당시의 객체 타입 기준으로 바인딩되는 것
System.out.println("==============================");
// **동적 바인딩 활용 방법**
Car[] arr = {
new Car("경유 엔진", "경유", 12),
new Tesla("전기 모터", "전기", 4, 50000),
new Spark("경차 엔진", "무연", 4, 0.3)
};
//arr 배열 요소가 참조하는 모든 객체의 필드 값 출력
for (int i =0 ; i<arr.length; i++) {
System.out.println(i + "번째 요소" +arr[i].toString());
//실행 전: String edu.kh.poly.ex1.model.vo.Car.toString() - 정적 바인딩
//실행 후: 각 객체의 오버라이딩된 toString() 호출됨 - 동적 바인딩
}
//**동적 바인딩의 장점**
//-업캐스팅 상태의 참조변수를 별도의 다운캐스팅 없이
// 자식의 오버라이딩된 메소드를 수행할 수 있다.
}
}
package edu.kh.poly.ex1.run;
import edu.kh.poly.ex1.model.sevice.PolyService;
public class PolyRun {
public static void main(String[] args) {
PolyService service = new PolyService();
// service.ex1();
// service.ex2();
// service.ex3();
// service.ex4();
service.ex5();
}
}