하나의 이름의 클래스나 메소드가 '여러 가지 형태의 동작을 하는 능력'
- 클래스의 다형성:
한 타입의 참조변수로 여러타입의 객체를 참조 가능.
조상클래스 타입의 참조변수로 자손클래스의 인스턴스를 참조가능한것- 메소드의 다형성:
메소드 오버로딩, 메소드 오버라이딩
package com.lec.java.oop01;
public class Polymorphism01Main {
public static void main(String[] args) {
System.out.println("다형성(Polymorphism)");
System.out.println();
// v1, c1, h1의 타입이 다르다
// 각각의 타입에 맞는 인스턴스 생성한 뒤 대입
Vehicle v1 = new Vehicle();
Car c1 = new Car();
HybridCar h1 = new HybridCar();
// 각각의 타입에 오버라이딩 된 메소드가 동작함
v1.displayInfo();
c1.displayInfo();
h1.displayInfo();
System.out.println();
// car1, car2는 동일한 Vehicle 타입변수 (각각 Car, HybridCar 객체를 담음)
Vehicle car1 = new Car(); // 조상 <- 자손 (가능)
Vehicle car2 = new HybridCar();
Car car3 = new HybridCar();
// HybridCar car7 = new Vehicle(); // 자손 <- 조상 (불가) ERROR : cannot convert from Vehicle to HybridCar
Object car8 = new Vehicle();
// car1 ~ car3 변수 타입에 관계없이
// 오버라디디디된 메소드가 '알아서' 동작한다.
car1.displayInfo(); // Car
car2.displayInfo(); // HybridCar()
car3.displayInfo(); // HybridCar()
System.out.println("\n 프로그램 종료");
} // end main()
// TODO
} // end class
package com.lec.java.oop01;
public class Vehicle {
private int speed;
public int getSpeed() {
return speed;
}
public void setSpeed(int speed) {
this.speed = speed;
}
public void displayInfo() {
System.out.println("--- Vehicle 정보 ---");
System.out.println("speed: " + speed);
}
}
package com.lec.java.oop01;
public class Car extends Vehicle {
private int oil;
public int getOil() {
return oil;
}
public void setOil(int oil) {
this.oil = oil;
}
@Override
public void displayInfo() {
System.out.println("--- Car 정보 ---");
System.out.println("speed: " + getSpeed());
System.out.println("oil: " + oil);
}
}
package com.lec.java.oop01;
public class HybridCar extends Car {
private int electricity;
public int getElectricity() {
return electricity;
}
public void setElectricity(int electricity) {
this.electricity = electricity;
}
@Override
public void displayInfo() {
System.out.println("--- HybridCar 정보 ---");
System.out.println("speed: " + getSpeed());
System.out.println("oil: " + getOil());
System.out.println("electricity:" + electricity);
}
}
- 다형성에 의해서, 자식타입 객체가 부모타입으로 자동 형변환 가능!
- 부모(조상)타입 만으로도 상속된 모~든 자손 타입들을 담을 수 있다.
public class Polymorphism02Main {
public static void main(String[] args) {
System.out.println("다형성의 사용 (유용성)");
// 다형성에 의해서, 자식타입 객체가 부모타입으로 자동 형변환 가능!
Vehicle car1 = new Vehicle();
Vehicle car2 = new Car();
Vehicle car3 = new HybridCar();
// 다형성의 유용성 1
// 부모타입으로 모든 자손 타입들을 담을 수 있다.
Vehicle [] car = new Vehicle[3];
car[0] = new Vehicle();
car[1] = new Car();
car[2] = new HybridCar();
for (int i = 0; i < car.length; i++) {
car[i].displayInfo(); // 조상타입 한가지로 한꺼번에 동작 가능
}
// 다형성이 없었다면? 각 타입별로 변수들을 만들고 따로따로 사용해야 하는 왕불편.
// Vehicle car1 = new Vehicle();
// Car car2 = new Car();
// HybridCar car3 = new HybridCar();
// car1.displayInfo();
// car2.displayInfo();
// car3.displayInfo();
// 다형성의 유용성 2
// 다형성의 유용함은 매개변수, 혹은 리턴 타입에도 적용된다
// println의 매개변수 Object의 참조변수가 넘겨지면,
// 내부적으로 해당 클래스의 toString() 메소드가 불리게 됨
System.out.println(car1); // print(Vehicle) ??? (x) -> BUT, println -> object 타입은 매개변수로 받을 수 있음
System.out.println(car2);
System.out.println(car3);
System.out.println();
// instanceof 연산자
// 용법: 변수/값 instanceof 클래스
// 결과: true / false
System.out.println(car1 instanceof Vehicle); // instance 우: 클래스명 / 좌: 변수나 값
System.out.println(car1 instanceof Car);
System.out.println(car2 instanceof Vehicle);
System.out.println(car2 instanceof HybridCar);
System.out.println();
Vehicle car4 = new Vehicle();
Car car5= new Car();
HybridCar car6 = new HybridCar();
driveCar(car4);
driveCar(car5);
driveCar(car6);
System.out.println("\n 프로그램 종료");
} // end main()
public static void driveCar(Vehicle v) {
v.setSpeed(100);
v.displayInfo();
}
} // end class
/* 코드만 보아도 알아요~
* A a = new B() ← A 는 B 의 조상타입
* R doSomethig(K p1){..} ← K 는 Z 의 조상타입
* Y y = doSomething(new Z()) ← Y 는 R 의 조상타입
*
*/
package com.lec.java.oop01;
public class Vehicle {
private int speed;
public int getSpeed() {
return speed;
}
public void setSpeed(int speed) {
this.speed = speed;
}
public void displayInfo() {
System.out.println("--- Vehicle 정보 ---");
System.out.println("speed: " + speed);
}
}
package com.lec.java.oop01;
public class Car extends Vehicle {
private int oil;
public int getOil() {
return oil;
}
public void setOil(int oil) {
this.oil = oil;
}
@Override
public void displayInfo() {
System.out.println("--- Car 정보 ---");
System.out.println("speed: " + getSpeed());
System.out.println("oil: " + oil);
}
}
package com.lec.java.oop01;
public class HybridCar extends Car {
private int electricity;
public int getElectricity() {
return electricity;
}
public void setElectricity(int electricity) {
this.electricity = electricity;
}
@Override
public void displayInfo() {
System.out.println("--- HybridCar 정보 ---");
System.out.println("speed: " + getSpeed());
System.out.println("oil: " + getOil());
System.out.println("electricity:" + electricity);
}
}
package com.lec.java.oop03;
public class Polymorphism03Main {
public static void main(String[] args) {
System.out.println("다형성의 어려움");
Vehicle car1 = new Vehicle();
Vehicle car2 = new Car();
Vehicle car3 = new HybridCar();
car2.setSpeed(10);
// car2는 Vehicle 타입으로 선언되어 있으므로,
// Vehicle 클래스에 정의된 메소드를 사용할 수 있다.
// 반면..
// car2.setOil(100); // 에러 : undefined for the type Vehicle
// car2는 Vehicle 타입으로 선언되어 있으므로,
// Vehicle이 아닌 다른 클래스(심지어 자식 클래스이더라도)에 정의된
// 메소드는 사용할 수 없다.
// 따라서, 실제로 Car 타입 인스턴스로 생성되긴 했지만,
// Vehicle 타입 참조변수는 Car 클래스에 있는 메소드를 사용할 수 없다.
car2.displayInfo();
// car2는 Vehicle 타입으로 선언되었으므로,
// displayInfo()는 Vehicle 클래스에서 정의된 메소드를 찾아가지만,
// 인스턴스가 생성될 때 Car 타입으로 생성되어서,
// Car 클래스에서 override된 displayInfo() 메소드가
// 부모 클래스의 displayInfo()를 덮어써 버리게 됨
// 결과는 Car의 정보를 출력하게 됨
((Car)car2).setOil(50);
// 실제로 Car 클래스의 인스턴스로 생성된 car2 변수는
// 형변환(casting)을 통해서 Car 타입으로 변환할 수 있고,
// Car 클래스에 정의된 메소드를 사용할 수 있다.
car2.displayInfo();
((Car)car1).setOil(10); // 오류 : ClassCastException - 클래스 형변환중에 발생된 오류 (Vehicle cannot be cast to class com.lec.java.oop03.Car)
// ClassCastException 발생:
// 실제로 Vehicle 클래스의 인스턴스로 생성된 car1을
// 자식 클래스인 Car로 강제 형변환을 하게 되면 문제가 발생할 수 있다.
// 예외는 setOil() 을 호출하는 과정이 아니라, 형변환 하는 과정에서 발생된다
System.out.println("\n 프로그램 종료");
} // end main()
} // end class