다형성 문서의 구현 코드다.
// 다형성: 필드 다형성(Car와 Tire), 매개변수 다형성(Driver와 Vehicle)
// 실행: docker run --rm -v "$PWD":/w -w /w eclipse-temurin:21-jdk java 29_java-polymorphism_impl.java
// 문서(29.다형성.md)에 실은 출력과 카드의 답을 직접 비교한다.
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
class PolymorphismCheck {
static String capture(Runnable r) {
PrintStream old = System.out;
ByteArrayOutputStream buf = new ByteArrayOutputStream();
System.setOut(new PrintStream(buf));
try { r.run(); } finally { System.setOut(old); }
return buf.toString();
}
static void check(String name, String got, String want) {
if (!got.equals(want)) throw new AssertionError(name + "\n--- got ---\n" + got + "--- want ---\n" + want);
System.out.println("ok: " + name);
}
public static void main(String[] args) {
// 필드 다형성: 문서의 Car
check("필드 다형성", capture(() -> {
Car myCar = new Car();
myCar.run();
myCar.tire = new KumhoTire();
myCar.run();
}), "Hankook Tire\nKumho Tire\n");
// 매개변수 다형성: 문서의 Driver
check("매개변수 다형성", capture(() -> {
Driver driver = new Driver();
Bus bus = new Bus();
Taxi taxi = new Taxi();
driver.drive(bus);
driver.drive(taxi);
}), "Bus is running.\nTaxi is running.\n");
// 새 구현 클래스를 더해도 Driver는 그대로다
check("새 클래스 Subway", capture(() -> new Driver().drive(new Subway())), "Subway is running.\n");
// 배열에 섞어 담고 같은 호출을 되풀이한다 (카드 C3)
check("카드 C3", capture(() -> {
Vehicle[] list = { new Taxi(), new Bus(), new Subway(), new Bus() };
for (Vehicle v : list) v.run();
}), "Taxi is running.\nBus is running.\nSubway is running.\nBus is running.\n");
// 보장하지 않는 것 1: 필드는 변수의 타입을 따른다
check("필드는 변수 타입을 따른다", capture(() -> {
Parent p = new Child();
System.out.println(p.name);
System.out.println(p.getName());
System.out.println(((Child) p).name);
}), "parent\nchild\nchild\n");
// 보장하지 않는 것 2: 정적 메서드는 변수의 타입을 따른다
check("정적 메서드는 변수 타입을 따른다", capture(() -> {
Parent p = new Child();
System.out.println(p.kind());
}), "Parent\n");
// 카드 C5
check("카드 C5", capture(() -> {
Car car = new Car();
Tire t = car.tire;
car.tire = new KumhoTire();
t.roll();
car.run();
}), "Hankook Tire\nKumho Tire\n");
System.out.println("ALL CHECKS PASSED");
}
}
interface Tire { void roll(); }
class HankookTire implements Tire {
@Override
public void roll() { System.out.println("Hankook Tire"); }
}
class KumhoTire implements Tire {
@Override
public void roll() { System.out.println("Kumho Tire"); }
}
class Car {
Tire tire = new HankookTire();
void run() { tire.roll(); }
}
interface Vehicle { void run(); }
class Driver {
public void drive(Vehicle vehicle) { vehicle.run(); }
}
class Bus implements Vehicle {
@Override
public void run() { System.out.println("Bus is running."); }
}
class Taxi implements Vehicle {
@Override
public void run() { System.out.println("Taxi is running."); }
}
class Subway implements Vehicle {
@Override
public void run() { System.out.println("Subway is running."); }
}
class Parent {
String name = "parent";
String getName() { return name; }
static String kind() { return "Parent"; }
}
class Child extends Parent {
String name = "child";
@Override
String getName() { return name; }
static String kind() { return "Child"; }
}