F-lab Java 1์ฃผ์ฐจ / Phase 2 / Unit 2.4 ๋ณธ๊ฒฉ ํ์ต ์๋ฃ
9-์น์ ๋ง์คํฐ ํ๋กฌํํธ ํ์์ผ๋ก ๊น์ด ํํค์น๋ค.์ ์ ์ง์: Unit 2.3 (์์๊ณผ ์์ฑ์ ์ฒด์ด๋)
๋ค์ Unit: 2.5 โ instanceof์ ํ๋ณํ์ด Unit์ ์๋ฏธ: OOP 4๋ ์์น์ ์ ์ . ๋ฉด์ ์์ ๊ฐ์ฅ ์์ฃผ ๋ฌป๋ ์์ญ.
๋คํ์ฑ์ ์ ํํ ์ดํดํ๋ฉด Spring DI, JPA, Strategy ํจํด ๋ฑ ๋ชจ๋ ํ๋ ์๋ฐ์ ํ ๋ ๊ฐ ์กํ๋ค.
์ ์๋์ด ๊ต์ค์์ ์ธ์นฉ๋๋ค:
"๋ชจ๋ ์ธ์ฌํ์ธ์!"
ํ์๋ค์ ๋ฐ์:
๊ฐ์ ๋ช ๋ น ("์ธ์ฌํ์ธ์") ์ธ๋ฐ ๋ค๋ฅธ ๋ฐ์ ์ด ๋์ต๋๋ค.
ํต์ฌ:
โ ์ด๊ฒ ๋คํ์ฑ โ ๊ฐ์ ๋ฉ์์ง์ ๊ฐ์ฒด๋ง๋ค ๋ค๋ฅธ ์๋ต.
๋น์ ์ด ํธํ ๋ฐฉ์ ๋ฆฌ๋ชจ์ปจ์ ๋ด ๋๋ค. ๋ฆฌ๋ชจ์ปจ์๋ "์ ์" ๋ฒํผ ์ด ์์ด์.
์ด ํธํ ์๋ ๋ค์ํ TV๊ฐ ์์ต๋๋ค:
๋น์ ์ด "์ ์" ๋ฒํผ์ ๋๋ฅด๋ฉด:
๋น์ ์ด TV ํ์ฌ๋ฅผ ์์์ผ ํ๋์? ์๋๋๋ค. ๊ทธ๋ฅ ๋๋ฅด๋ฉด ๋จ.
๋น์ : ๋ฆฌ๋ชจ์ปจ.์ ์() // โ ๊ฐ์ ๋ช
๋ น
โ
TV๊ฐ ์๊ธฐ ๋ฐฉ์๋๋ก ์ฒ๋ฆฌ:
- LG: LCD ๋ฐฉ์์ผ๋ก ์ผ์ง
- ์ผ์ฑ: OLED ๋ฐฉ์์ผ๋ก ์ผ์ง
- ์๋: HDR ๋ฐฉ์์ผ๋ก ์ผ์ง
- ๋ธ๋ผ์ด๊ด: ์ง๊ณต๊ด ๋ฐฉ์์ผ๋ก ์ผ์ง
โ ๋คํ์ฑ์ ๋ณธ์ง.
๋คํ์ฑ(Polymorphism) = ๊ทธ๋ฆฌ์ค์ด ์ด์
์๋ฐ์์:
"๊ฐ์ ๋ฉ์๋ ํธ์ถ์ด ๊ฐ์ฒด์ ๋ฐ๋ผ ๋ค๋ฅธ ๋์์ ํ๋ ๊ฒ"
| ๋น์ ์์ | ์๋ฐ ๋คํ์ฑ |
|---|---|
| "์ธ์ฌํ์ธ์" ๋ช ๋ น | ๋ฉ์๋ ํธ์ถ |
| ํ์๋ค | ๋ค์ํ ๊ฐ์ฒด |
| ํ๊ตญ์/๋ฏธ๊ตญ์/์ผ๋ณธ์ ์ธ์ฌ | ์ค๋ฒ๋ผ์ด๋๋ ๋ฉ์๋ |
| ์ ์๋์ด ๊ตญ์ ๋ชจ๋ฆ | ์ปดํ์ผ ์ ํ์ ๋ชจ๋ฆ |
| ํ์์ด ์์์ ์ธ์ฌ | ๋ฐํ์์ ๊ฐ์ฒด๊ฐ ๊ฒฐ์ |
์์(Unit 2.3) ๋ง ์๊ณ ๋คํ์ฑ์ด ์๋ค๋ฉด, ๊ฐ์ฒด๋ณ ์ฒ๋ฆฌ๋ฅผ ํ๋ ค๋ฉด ๋งค๋ฒ if ๋ถ๊ธฐ๋ฅผ ์จ์ผ ํฉ๋๋ค.
// ๋คํ์ฑ ์์ด โ ๋งค๋ฒ ํ์
ํ์ธ
public void printSound(Animal animal) {
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
System.out.println(dog.bark());
} else if (animal instanceof Cat) {
Cat cat = (Cat) animal;
System.out.println(cat.meow());
} else if (animal instanceof Cow) {
Cow cow = (Cow) animal;
System.out.println(cow.moo());
} else if (animal instanceof Duck) {
Duck duck = (Duck) animal;
System.out.println(duck.quack());
}
// ์ ๋๋ฌผ ์ถ๊ฐ ์ ๋ else if โ
}
๋ฌธ์ :
oink()) ๊น์ง ์์์ผ ํจ๊ฐ์ฒด์งํฅ์ ๋ณธ๊ฒฉ์ ์ ๋ฆฝ์ Smalltalk-80 (1980) ์์ ์ด๋ฃจ์ด์ก์ต๋๋ค. Smalltalk๊ฐ ๊ฐ์กฐํ ํต์ฌ ๊ฐ๋ :
"๊ฐ์ ๋ฉ์์ง๋ฅผ ๋ณด๋ด๋ฉด, ๋ฐ๋ ๊ฐ์ฒด๊ฐ ์์์ ์ฒ๋ฆฌํ๋ค"
// ๋คํ์ฑ ํ์ฉ โ if ์ฌ๋ผ์ง
public void printSound(Animal animal) {
System.out.println(animal.makeSound()); // โ ๊ฐ์ฒด๊ฐ ์์์!
}
// ์ ๋๋ฌผ ์ถ๊ฐํด๋ ์ด ์ฝ๋๋ ์ ๋ฐ๋ โ
ํ์ํ ๊ฒ:
makeSound() ๋ฉ์๋๋ฅผ ๊ฐ์ง (์ธํฐํ์ด์ค ํต์ผ)Animal ํ์
์ผ๋ก ๋ค๋ฃจ๊ธฐ๋ง ํ๋ฉด ๋จ์๋ฐ๊ฐ 1995๋ ๋ฑ์ฅ ์ ๋คํ์ฑ์ 1๊ธ ์๋ฏผ์ผ๋ก ๋์ ํ์ต๋๋ค. ๊ทธ ๊ฒฐ๊ณผ:
Spring ์ํ๊ณ์ ํ ๋:
List<UserRepository> repos; // ์ด๋ค ๊ตฌํ์ฒด๋ OK
NotificationService service; // ์ธํฐํ์ด์ค๋ง ์๋ฉด ๋
JPA:
@Entity ํด๋์ค๋ฅผ ๋คํ์ ์ผ๋ก ์ฒ๋ฆฌ (Inheritance Strategies)
๋์์ธ ํจํด:
โ ํ๋ ์๋ฐ = ๋คํ์ฑ ์์ ์ธ์์ง ๊ฑฐ๋ํ ํ.
"๋คํ์ฑ์ด ์์ผ๋ฉด ๊ฐ์ฒด์งํฅ์ ์ ์ฐจ์งํฅ๊ณผ ๋ค๋ฅผ ๊ฒ ์๋ค."
์บก์ํ + ์์๋ง ์์ด๋ ๋ฐ์ดํฐ๋ฅผ ๋ณดํธํ๊ณ ์ฝ๋๋ฅผ ์ฌ์ฌ์ฉํ ์ ์์ง๋ง, ์ ์ข ๋ฅ ์ถ๊ฐ ์๋ง๋ค ๊ธฐ์กด ์ฝ๋๋ฅผ ์์ ํด์ผ ํ๋ค.
๋คํ์ฑ์ด ์์ด์ผ "๊ธฐ์กด ์ฝ๋๋ฅผ ์ ๊ฑด๋๋ฆฌ๊ณ ํ์ฅ" ์ด ๊ฐ๋ฅ โ ์ด๊ฒ SOLID์ OCP (๊ฐ๋ฐฉ-ํ์ ์์น) ์ ํต์ฌ.
๋คํ์ฑ์ด ์์ ๋์ ๊ตฌ์ฒด์ ๋ฌธ์ ๋ฅผ ILIC ์๋๋ฆฌ์ค๋ก ๋ณด๊ฒ ์ต๋๋ค.
ILIC๊ฐ ๋ค์ํ ๊ณ ๊ฐ ๋ฑ๊ธ๋ณ๋ก ๋ค๋ฅธ ํ ์ธ์จ์ ์ ์ฉํ๋ค๊ณ ํฉ์๋ค:
public class Customer {
private String level; // "NORMAL", "VIP", "PARTNER", "STUDENT"
private String name;
private String email;
public String getLevel() { return level; }
}
public class FareService {
// ํ ์ธ ๊ณ์ฐ โ if ์ง์ฅ
public int calculateDiscount(Customer customer, int amount) {
if (customer.getLevel().equals("NORMAL")) {
return 0;
} else if (customer.getLevel().equals("VIP")) {
return amount * 20 / 100;
} else if (customer.getLevel().equals("PARTNER")) {
return amount * 30 / 100;
} else if (customer.getLevel().equals("STUDENT")) {
return amount * 15 / 100;
}
return 0;
}
// ์๋ฆผ ๋ฉ์์ง โ ๋ if ์ง์ฅ
public String getNotificationGreeting(Customer customer) {
if (customer.getLevel().equals("NORMAL")) {
return "์๋
ํ์ธ์";
} else if (customer.getLevel().equals("VIP")) {
return "VIP ๊ณ ๊ฐ๋";
} else if (customer.getLevel().equals("PARTNER")) {
return "ํํธ๋๋";
} else if (customer.getLevel().equals("STUDENT")) {
return "ํ์ ๊ณ ๊ฐ๋";
}
return "๊ณ ๊ฐ๋";
}
// ์ถ๊ฐ ํํ ๊ฐ๋ฅ ์ฌ๋ถ โ ๋๋ if ์ง์ฅ
public boolean canApplyExtraBenefit(Customer customer) {
if (customer.getLevel().equals("VIP")) return true;
if (customer.getLevel().equals("PARTNER")) return true;
return false;
}
// ๋ฉค๋ฒ์ญ ํฌ์ธํธ ์ ๋ฆฝ๋ฅ โ ๋๋๋ if ์ง์ฅ
public double getPointAccumulationRate(Customer customer) {
if (customer.getLevel().equals("VIP")) return 0.05;
if (customer.getLevel().equals("PARTNER")) return 0.10;
if (customer.getLevel().equals("STUDENT")) return 0.03;
return 0.01;
}
// ... ๋ฑ๊ธ ๊ด๋ จ ๋ฉ์๋ 30๊ฐ ๋ชจ๋ if ์ง์ฅ โ
}
public int calculateDiscount(Customer customer, int amount) {
if (...) return 0;
else if (...) return amount * 20 / 100;
else if (...) return amount * 30 / 100;
else if (...) return amount * 15 / 100;
else if (customer.getLevel().equals("PLATINUM")) { // โ ์ถ๊ฐ
return amount * 25 / 100;
}
return 0;
}
public String getNotificationGreeting(Customer customer) {
// ... ๊ธฐ์กด ๋ถ๊ธฐ๋ค ...
else if (customer.getLevel().equals("PLATINUM")) { // โ ๋ ์ถ๊ฐ
return "ํ๋ํฐ๋ ๊ณ ๊ฐ๋";
}
return "๊ณ ๊ฐ๋";
}
// 30๊ฐ ๋ฉ์๋์ ๋ชจ๋ PLATINUM ๋ถ๊ธฐ ์ถ๊ฐ โ
// ํ ๊ตฐ๋ฐ๋ผ๋ ๋น ๋จ๋ฆฌ๋ฉด ๋ฒ๊ทธ โ
// ์ ๊ฐ๋ฐ์ ์
์ฅ์์ "PLATINUM ์ถ๊ฐํ๋ ค๋ฉด ์ด๋ ์ด๋ ์์ ?" ์ถ์ ๋ถ๊ฐ โ
๋ฌธ์ ์ ๋ฆฌ:
1. ์์ ์์ญ ํญ๋ฐ โ 1๊ฐ ์ถ๊ฐ์ 30๊ฐ ์์
2. ๋ฒ๊ทธ ์ํ โ ํ ๊ณณ ๋น ๋จ๋ฆผ
3. ์ถ์ ๋ถ๊ฐ โ "์ด ๋ฑ๊ธ ๊ด๋ จ ๋ก์ง์ด ์ด๋ ์๋?"
4. ํ
์คํธ ํญ์ฆ โ 30๊ฐ ๋ฉ์๋ ร 5๊ฐ ๋ฑ๊ธ = 150๊ฐ ์ผ์ด์ค
5. OCP ์๋ฐ โ "ํ์ฅ์๋ ์ด๋ ค์๊ณ , ์์ ์๋ ๋ซํ์์ด์ผ" ์ ์ ๋ฐ๋
// ๋ถ๋ชจ โ ์ถ์ ํด๋์ค
public abstract class Customer {
protected String name;
protected String email;
// ์ถ์ ๋ฉ์๋ โ ์์์ด ๋ฐ๋์ ๊ตฌํ
public abstract int calculateDiscountRate();
public abstract String getNotificationGreeting();
public abstract boolean canApplyExtraBenefit();
public abstract double getPointAccumulationRate();
// ๊ณตํต ํ๋
public int calculateDiscount(int amount) {
return amount * calculateDiscountRate() / 100;
}
}
// ๊ฐ ๋ฑ๊ธ โ ์๊ธฐ ๋ฐฉ์๋๋ก ๊ตฌํ
public class NormalCustomer extends Customer {
@Override public int calculateDiscountRate() { return 0; }
@Override public String getNotificationGreeting() { return "์๋
ํ์ธ์"; }
@Override public boolean canApplyExtraBenefit() { return false; }
@Override public double getPointAccumulationRate() { return 0.01; }
}
public class VipCustomer extends Customer {
@Override public int calculateDiscountRate() { return 20; }
@Override public String getNotificationGreeting() { return "VIP ๊ณ ๊ฐ๋"; }
@Override public boolean canApplyExtraBenefit() { return true; }
@Override public double getPointAccumulationRate() { return 0.05; }
}
public class PartnerCustomer extends Customer {
@Override public int calculateDiscountRate() { return 30; }
@Override public String getNotificationGreeting() { return "ํํธ๋๋"; }
@Override public boolean canApplyExtraBenefit() { return true; }
@Override public double getPointAccumulationRate() { return 0.10; }
}
public class StudentCustomer extends Customer {
@Override public int calculateDiscountRate() { return 15; }
@Override public String getNotificationGreeting() { return "ํ์ ๊ณ ๊ฐ๋"; }
@Override public boolean canApplyExtraBenefit() { return false; }
@Override public double getPointAccumulationRate() { return 0.03; }
}
Service โ if ์ง์ฅ ์ฌ๋ผ์ง:
public class FareService {
public int calculateDiscount(Customer customer, int amount) {
return customer.calculateDiscount(amount); // โ ๊ฐ์ฒด์ ์์
}
public String getNotificationGreeting(Customer customer) {
return customer.getNotificationGreeting(); // โ ๊ฐ์ฒด์ ์์
}
public boolean canApplyExtraBenefit(Customer customer) {
return customer.canApplyExtraBenefit(); // โ ๊ฐ์ฒด์ ์์
}
public double getPointAccumulationRate(Customer customer) {
return customer.getPointAccumulationRate(); // โ ๊ฐ์ฒด์ ์์
}
}
public class PlatinumCustomer extends Customer {
@Override public int calculateDiscountRate() { return 25; }
@Override public String getNotificationGreeting() { return "ํ๋ํฐ๋ ๊ณ ๊ฐ๋"; }
@Override public boolean canApplyExtraBenefit() { return true; }
@Override public double getPointAccumulationRate() { return 0.08; }
}
// ๋! โ
// ๊ธฐ์กด FareService ์ฝ๋๋ ํ ์ค๋ ์ ๋ฐ๋
// ์ฌ์ฉ ์:
Customer platinum = new PlatinumCustomer();
fareService.calculateDiscount(platinum, 10000); // ์๋์ผ๋ก PlatinumCustomer.calculateDiscount() ํธ์ถ
| ํญ๋ชฉ | ๋คํ์ฑ ์์ด | ๋คํ์ฑ ํ์ฉ |
|---|---|---|
| ์ ๋ฑ๊ธ ์ถ๊ฐ ์ ์์ | 30๊ฐ ๋ฉ์๋ | 1๊ฐ ํด๋์ค |
| ๋ฒ๊ทธ ์ํ | ๋งค์ฐ ๋์ | ๋ฎ์ |
| ์ฝ๋ ์ถ์ | ์ด๋ ค์ | ์ฌ์ (๊ฐ ๋ฑ๊ธ ํด๋์ค๋ง ๋ณด๋ฉด ๋จ) |
| ํ ์คํธ | 150๊ฐ ์ผ์ด์ค | ๋ฑ๊ธ๋ณ + Service ๋ณ๋ |
| OCP ์ค์ | โ | โ |
| ์ ๊ฐ๋ฐ์ ์จ๋ณด๋ฉ | ์ง์ฅ | ๋ช ํ |
โ ์ด๊ฒ ๋คํ์ฑ์ ์ง์ง ๊ฐ์น.
๋ถ๋ชจํ์
๋ณ์ = new ์์ํ์
();
๋ณ์.๋ฉ์๋(); // ์์์ ๋ฉ์๋๊ฐ ํธ์ถ๋จ
์์:
Animal animal = new Dog(); // โ
๋ถ๋ชจ ํ์
= ์์ ๊ฐ์ฒด
animal.makeSound(); // "๋ฉ๋ฉ!" โ Dog์ ๋ฉ์๋
์ ์ด๊ฒ ๊ฐ๋ฅํ๊ฐ?:
Dog ๋ Animal ์ ํ ์ข
๋ฅ (is-a ๊ด๊ณ)Dog ๋ Animal ์ ์ฝ์ (๋ฉ์๋ ์๊ทธ๋์ฒ) ์ ์งํดAnimal ํ์
๋ณ์์ ๋ด์ ์ ์์public abstract class Animal { public abstract void makeSound(); }
public class Dog extends Animal { @Override public void makeSound() { ... } }
Animal a = new Dog(); // โ
public interface Drawable { void draw(); }
public class Circle implements Drawable { @Override public void draw() { ... } }
public class Square implements Drawable { @Override public void draw() { ... } }
Drawable d = new Circle(); // โ
Drawable d2 = new Square(); // โ
โ ํ๋ ์๋ฐ์์ ๊ฐ์ฅ ๋ง์ด ์ฐ๋ ๋ฐฉ์.
public abstract class Shape {
public abstract double area();
}
public class Circle extends Shape {
@Override public double area() { ... }
}
Shape s = new Circle(); // โ
List<Animal> animals = new ArrayList<>();
animals.add(new Dog());
animals.add(new Cat());
animals.add(new Cow());
animals.add(new Duck());
for (Animal animal : animals) {
animal.makeSound(); // ๊ฐ์ ์๊ธฐ ๋ฐฉ์์ผ๋ก
}
// ์ถ๋ ฅ:
// ๋ฉ๋ฉ!
// ์ผ์น~
// ์๋งค
// ๊ฝฅ๊ฝฅ
โ ํ์ ์ ์ผ์ผ์ด ์ ๊ฒฝ ์ฐ์ง ์๊ณ ํต์ผ๋ ์ฒ๋ฆฌ.
public class AnimalShelter {
public void care(Animal animal) { // โ Animal ํ์
animal.makeSound();
animal.eat();
animal.sleep();
}
}
AnimalShelter shelter = new AnimalShelter();
shelter.care(new Dog()); // โ
shelter.care(new Cat()); // โ
shelter.care(new Penguin()); // โ
โ ์ ๋๋ฌผ๋ OK
โ AnimalShelter ์ฝ๋ ํ ์ค๋ ์ ๊ฑด๋๋ฆฌ๊ณ ์ ๋๋ฌผ ์ฒ๋ฆฌ.
public class AnimalFactory {
public static Animal create(String type) {
switch (type) {
case "dog": return new Dog();
case "cat": return new Cat();
default: throw new IllegalArgumentException();
}
}
}
Animal a = AnimalFactory.create("dog");
a.makeSound(); // "๋ฉ๋ฉ!"
โ Factory ํจํด์ ํ ๋.
๋ฐ์ธ๋ฉ(Binding):
"๋ฉ์๋ ํธ์ถ๊ณผ ์ค์ ๋ฉ์๋ ์ฝ๋๋ฅผ ์ฐ๊ฒฐํ๋ ๊ฒ"
๋ ์ข ๋ฅ:
JVM์ ๊ฐ ํด๋์ค๋ง๋ค VMT (Virtual Method Table) ๋ฅผ ๊ฐ์ง๋๋ค.
public class Animal {
public void makeSound() { System.out.println("๋๋ฌผ ์๋ฆฌ"); }
public void eat() { System.out.println("๋จน๋ค"); }
}
public class Dog extends Animal {
@Override
public void makeSound() { System.out.println("๋ฉ๋ฉ!"); }
public void wagTail() { System.out.println("๊ผฌ๋ฆฌ ํ๋ค๊ธฐ"); }
}
JVM์ด ๋ง๋๋ VMT:
Animal์ VMT:
[0] makeSound โ Animal.makeSound
[1] eat โ Animal.eat
[2] toString โ Object.toString
[3] equals โ Object.equals
...
Dog์ VMT (Animal ์์ + ์ค๋ฒ๋ผ์ด๋ + ์ถ๊ฐ):
[0] makeSound โ Dog.makeSound โ ์ค๋ฒ๋ผ์ด๋๋จ โญ
[1] eat โ Animal.eat โ ์์๋ฐ์
[2] toString โ Object.toString
[3] equals โ Object.equals
[4] wagTail โ Dog.wagTail โ Dog๋ง์ ์ ๋ฉ์๋
Animal animal = new Dog();
animal.makeSound();
JVM ๋ด๋ถ ๋์:
1. animal.makeSound() ํธ์ถ
โ
2. JVM: "animal ๋ณ์์ ์ปดํ์ผ ํ์ ํ์
์? Animal"
โ
3. JVM: "animal์ด ๊ฐ๋ฆฌํค๋ ์ค์ ๊ฐ์ฒด๋? Dog ์ธ์คํด์ค"
โ
4. JVM: "Dog ์ธ์คํด์ค์ ํด๋์ค ์ ๋ณด (Class) ํ์ธ"
โ
5. JVM: "Dog์ VMT์์ makeSound ์ฐพ๊ธฐ"
โ
6. VMT[0] = Dog.makeSound โ ๋ฐ๊ฒฌ
โ
7. Dog.makeSound() ์คํ โ "๋ฉ๋ฉ!"
ํต์ฌ:
"์ปดํ์ผ ํ์ ํ์ ์ 'Animal' ์ด์ง๋ง, ๋ฐํ์์ ์ค์ ๊ฐ์ฒด (Dog) ์ ๋ฉ์๋๊ฐ ํธ์ถ๋จ"
โ ์ด๊ฒ ๋์ ๋ฐ์ธ๋ฉ.
Animal animal = new Dog();
// โ โ
// ์ปดํ์ผ ํ์ ๋ฐํ์ ํ์
// ํ์
(์ฐธ์กฐ) (์ค์ ๊ฐ์ฒด)
์ด๋ค ์ฐจ์ด๋ฅผ ๋ง๋๋?
Animal animal = new Dog();
animal.makeSound(); // โ
Animal์ ์์
animal.eat(); // โ
Animal์ ์์
animal.wagTail(); // โ ์ปดํ์ผ ์๋ฌ โ Animal์๋ wagTail ์์
โ ์ปดํ์ผ๋ฌ๋ ๋ณ์์ ์ ์ธ ํ์ (Animal) ๋ง ๋ด.
Animal animal = new Dog();
animal.makeSound(); // "๋ฉ๋ฉ!" โ Dog์ ๋ฉ์๋
โ JVM์ ์ค์ ๊ฐ์ฒด (Dog) ์ ๋ฉ์๋ ํธ์ถ.
public class Parent {
public static void staticMethod() { System.out.println("Parent static"); }
public void instanceMethod() { System.out.println("Parent instance"); }
}
public class Child extends Parent {
public static void staticMethod() { System.out.println("Child static"); }
@Override
public void instanceMethod() { System.out.println("Child instance"); }
}
Parent p = new Child();
p.staticMethod(); // โ
p.instanceMethod(); // โ
๊ฒฐ๊ณผ:
Parent static โ ์ ์ ๋ฐ์ธ๋ฉ โ ๋ณ์ ํ์
(Parent)์ผ๋ก ๊ฒฐ์
Child instance โ ๋์ ๋ฐ์ธ๋ฉ โ ์ค์ ๊ฐ์ฒด(Child)๋ก ๊ฒฐ์
ํต์ฌ ์ฐจ์ด โญ :
public class Parent {
public String name = "Parent";
}
public class Child extends Parent {
public String name = "Child";
}
Parent p = new Child();
System.out.println(p.name); // โ
๊ฒฐ๊ณผ: "Parent" โ ํ๋๋ ๋ณ์ ํ์
์ผ๋ก ๊ฒฐ์ .
โ ํ๋๋ ๋คํ์ฑ X, ๋ฉ์๋๋ง ๋คํ์ฑ O.
ํด๊ฒฐ: getter ๋ฉ์๋ ์ฌ์ฉ:
public class Parent {
private String name = "Parent";
public String getName() { return name; } // ๋ฉ์๋๋ ๋คํ์ฑ O
}
public class Child extends Parent {
private String name = "Child";
@Override
public String getName() { return name; }
}
Parent p = new Child();
System.out.println(p.getName()); // "Child" โ
โ ํ๋๋ ์ง์ ๋ ธ์ถํ์ง ๋ง๊ณ ๋ฉ์๋๋ก โ ์บก์ํ + ๋คํ์ฑ.
public class Parent {
public Parent() {
init(); // โ ๏ธ ์ํ!
}
public void init() { System.out.println("Parent init"); }
}
public class Child extends Parent {
private String name = "Child";
@Override
public void init() {
System.out.println("Child init: " + name.length()); // NPE!
}
}
new Child();
์ NPE?:
1. new Child() โ Parent ์์ฑ์ ๋จผ์ ์คํ
2. Parent ์์ฑ์์์ init() ํธ์ถ
3. ๋คํ์ฑ ์ผ๋ก Child์ init() ์คํ
4. ๊ทธ๋ฌ๋ ์ด ์์ ์ Child์ name ์ ์์ง ์ด๊ธฐํ X (null)
5. name.length() โ NPE
๊ตํ: ์์ฑ์์์ ์ค๋ฒ๋ผ์ด๋ ๊ฐ๋ฅํ ๋ฉ์๋ ํธ์ถ X.
ILIC ์ด์ ์์คํ ์์ ๋คํ์ฑ์ ํ์ฉํ๋ ๋ค์ํ ํจํด.
// ์ธํฐํ์ด์ค โ ์๋ฆผ์ ์ฝ์
public interface NotificationSender {
void send(String message, String recipient);
}
// ๋ค์ํ ๊ตฌํ์ฒด
public class EmailSender implements NotificationSender {
@Override
public void send(String message, String recipient) {
System.out.println("[Email] " + recipient + ": " + message);
// ์ค์ ์ด๋ฉ์ผ ๋ฐ์ก ๋ก์ง
}
}
public class SmsSender implements NotificationSender {
@Override
public void send(String message, String recipient) {
System.out.println("[SMS] " + recipient + ": " + message);
}
}
public class SlackSender implements NotificationSender {
@Override
public void send(String message, String recipient) {
System.out.println("[Slack] " + recipient + ": " + message);
}
}
public class KakaoTalkSender implements NotificationSender {
@Override
public void send(String message, String recipient) {
System.out.println("[KakaoTalk] " + recipient + ": " + message);
}
}
// ์ฌ์ฉ โ ๋คํ์ฑ ํ์ฉ
public class NotificationService {
private final List<NotificationSender> senders;
public NotificationService(List<NotificationSender> senders) {
this.senders = senders;
}
public void notifyAll(String message, String recipient) {
for (NotificationSender sender : senders) {
sender.send(message, recipient); // โ ๋คํ์ฑ!
}
}
}
// ํธ์ถ
List<NotificationSender> senders = List.of(
new EmailSender(),
new SmsSender(),
new SlackSender(),
new KakaoTalkSender()
);
NotificationService service = new NotificationService(senders);
service.notifyAll("์ด์์ด ๋ฑ๋ก๋์์ต๋๋ค", "alice@example.com");
ํจ๊ณผ:
// ๋ถ๋ชจ ์ถ์ ํด๋์ค
public abstract class Fare {
protected Long id;
protected int amount;
protected FareStatus status;
public Fare(Long id, int amount) {
this.id = id;
this.amount = amount;
this.status = FareStatus.DRAFT;
}
// ์ถ์ ๋ฉ์๋ โ ์ข
๋ฅ๋ณ๋ก ๋ค๋ฆ
public abstract int calculateTotal();
public abstract String getDescription();
// ๊ณตํต ๋ฉ์๋ โ ๋ชจ๋ ์ด์์ ๊ณตํต
public void submit() {
if (status != FareStatus.DRAFT) {
throw new IllegalStateException();
}
status = FareStatus.SUBMITTED;
}
}
public class StandardFare extends Fare {
public StandardFare(Long id, int amount) {
super(id, amount);
}
@Override
public int calculateTotal() {
return amount;
}
@Override
public String getDescription() {
return "์ผ๋ฐ ์ด์";
}
}
public class UrgentFare extends Fare {
private int urgentFee;
public UrgentFare(Long id, int amount, int urgentFee) {
super(id, amount);
this.urgentFee = urgentFee;
}
@Override
public int calculateTotal() {
return amount + urgentFee;
}
@Override
public String getDescription() {
return "๊ธด๊ธ ์ด์ (๊ธด๊ธ๋น: " + urgentFee + ")";
}
}
public class InternationalFare extends Fare {
private String currency;
private double exchangeRate;
public InternationalFare(Long id, int amount, String currency, double rate) {
super(id, amount);
this.currency = currency;
this.exchangeRate = rate;
}
@Override
public int calculateTotal() {
return (int)(amount * exchangeRate);
}
@Override
public String getDescription() {
return "๊ตญ์ ์ด์ (" + currency + " " + exchangeRate + ")";
}
}
// ์ฌ์ฉ โ ๋คํ์ฑ์ผ๋ก ํต์ผ๋ ์ฒ๋ฆฌ
List<Fare> fares = List.of(
new StandardFare(1L, 50000),
new UrgentFare(2L, 50000, 10000),
new InternationalFare(3L, 100, "USD", 1300.0)
);
int totalRevenue = 0;
for (Fare fare : fares) {
totalRevenue += fare.calculateTotal(); // โ ๋คํ์ฑ!
System.out.println(fare.getDescription());
}
์ถ๋ ฅ:
์ผ๋ฐ ์ด์
๊ธด๊ธ ์ด์ (๊ธด๊ธ๋น: 10000)
๊ตญ์ ์ด์ (USD 1300.0)
โ ์ ์ด์ ์ข ๋ฅ ์ถ๊ฐ ์ ์ด ์ฝ๋๋ ํ ์ค๋ ์ ๋ฐ๋.
public interface PaymentMethod {
void process(int amount);
String getMethodName();
}
public class CreditCardPayment implements PaymentMethod {
private String cardNumber;
public CreditCardPayment(String cardNumber) {
this.cardNumber = cardNumber;
}
@Override
public void process(int amount) {
System.out.println("์ ์ฉ์นด๋ " + cardNumber + " ๋ก " + amount + "์ ๊ฒฐ์ ");
}
@Override
public String getMethodName() {
return "์ ์ฉ์นด๋";
}
}
public class BankTransferPayment implements PaymentMethod {
private String accountNumber;
public BankTransferPayment(String accountNumber) {
this.accountNumber = accountNumber;
}
@Override
public void process(int amount) {
System.out.println("๊ณ์ข " + accountNumber + " ์์ " + amount + "์ ์ด์ฒด");
}
@Override
public String getMethodName() {
return "๊ณ์ข์ด์ฒด";
}
}
public class KakaoPayPayment implements PaymentMethod {
@Override
public void process(int amount) {
System.out.println("์นด์นด์คํ์ด๋ก " + amount + "์ ๊ฒฐ์ ");
}
@Override
public String getMethodName() {
return "์นด์นด์คํ์ด";
}
}
// ์ฌ์ฉ
public class CheckoutService {
public void checkout(Fare fare, PaymentMethod paymentMethod) {
int total = fare.calculateTotal();
paymentMethod.process(total);
System.out.println(paymentMethod.getMethodName() + " ๊ฒฐ์ ์๋ฃ");
}
}
CheckoutService service = new CheckoutService();
Fare fare = new StandardFare(1L, 50000);
service.checkout(fare, new CreditCardPayment("1234-5678"));
service.checkout(fare, new BankTransferPayment("110-1234"));
service.checkout(fare, new KakaoPayPayment());
ํจ๊ณผ:
// List ์ธํฐํ์ด์ค โ ๋ค์ํ ๊ตฌํ์ฒด
List<String> arrayList = new ArrayList<>(); // ๋น ๋ฅธ ์กฐํ
List<String> linkedList = new LinkedList<>(); // ๋น ๋ฅธ ์ฝ์
/์ญ์
List<String> immutableList = List.of("a", "b"); // ๋ถ๋ณ
// ๋์ผํ List ํ์
์ผ๋ก ํต์ผ๋ ์ฒ๋ฆฌ
public void process(List<String> list) {
for (String item : list) {
System.out.println(item);
}
}
process(arrayList); // OK
process(linkedList); // OK
process(immutableList); // OK
์๋ฐ ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ฒด๊ฐ ๋คํ์ฑ์ ๊ฑฐ๋ํ ์์:
Collection โ List, Set, QueueList โ ArrayList, LinkedList, VectorMap โ HashMap, TreeMap, LinkedHashMapโ ์ธํฐํ์ด์ค๋ก ์ฝ์, ๊ตฌํ์ฒด๋ ์์ .
๋ฏธ๋ ํ์ต ์ฃผ์ฐจ์ด์ง๋ง ๋ฏธ๋ฆฌ๋ณด๊ธฐ:
// ์ธํฐํ์ด์ค
public interface UserRepository {
User findById(Long id);
}
// ๋ค์ํ ๊ตฌํ
@Repository
public class JpaUserRepository implements UserRepository { ... }
// ๋๋
@Repository
public class MongoUserRepository implements UserRepository { ... }
// ์ฌ์ฉ
@Service
public class UserService {
private final UserRepository repository; // ์ธํฐํ์ด์ค ํ์
public UserService(UserRepository repository) {
this.repository = repository;
}
public User getUser(Long id) {
return repository.findById(id); // ์ด๋ ๊ตฌํ์ด๋ OK
}
}
Spring์ด ์๋์ผ๋ก:
UserRepository ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ ๋น์ ์ฐพ์UserService ์ ์๋ ์ฃผ์
โ Spring ์ ์ฒด๊ฐ ๋คํ์ฑ ์์ ์ค๊ณ๋จ. 5์ฃผ์ฐจ์์ ๋ณธ๊ฒฉ ํ์ต.
public class Animal {
public void makeSound() { ... }
}
public class Dog extends Animal {
public void makesound() { // ์คํ: 's' ๊ฐ ์๋ฌธ์
System.out.println("๋ฉ๋ฉ");
}
}
Animal a = new Dog();
a.makeSound(); // "๋๋ฌผ ์๋ฆฌ" โ ์ค๋ฒ๋ผ์ด๋ ์ ๋จ! โ
ํด๊ฒฐ: ๋ชจ๋ ์ค๋ฒ๋ผ์ด๋์ @Override ํ์.
@Override
public void makesound() { // ์ปดํ์ผ ์๋ฌ ์ฆ์ ๋ฐ๊ฒฌ
...
}
public class Parent {
public String name = "Parent";
}
public class Child extends Parent {
public String name = "Child";
}
Parent p = new Child();
System.out.println(p.name); // "Parent" โ ํ๋๋ ๋ณ์ ํ์
์ผ๋ก!
โ ํ๋๋ ๋คํ์ฑ X. ๋ฉ์๋๋ง ๋คํ์ฑ.
ํด๊ฒฐ: ํ๋๋ฅผ private์ผ๋ก + getter ๋ฉ์๋ ์ฌ์ฉ.
public class Parent {
public static void method() { System.out.println("Parent"); }
}
public class Child extends Parent {
public static void method() { System.out.println("Child"); }
}
Parent p = new Child();
p.method(); // "Parent" โ static์ ์ปดํ์ผ ์ ๊ฒฐ์ !
์?: static ๋ฉ์๋๋ ์ธ์คํด์ค๊ฐ ์๋ ํด๋์ค์ ์ํจ. ๋คํ์ฑ ์ ์ฉ X.
โ static ๋ฉ์๋๋ ์จ๊น(hiding) ์ผ ๋ฟ ์ค๋ฒ๋ผ์ด๋ X.
ํ: static ๋ฉ์๋๋ ํด๋์ค๋ช ์ผ๋ก ํธ์ถ ๊ถ์ฅ:
Parent.method(); // ๋ช
์์
Child.method();
// โ ๋คํ์ฑ์ ์ธ๋ฉดํ ์ฝ๋
public void process(Animal animal) {
if (animal instanceof Dog) {
((Dog) animal).bark();
} else if (animal instanceof Cat) {
((Cat) animal).meow();
}
}
โ ๋คํ์ฑ์ ์ฌ์ฉํ์ง ์๋ ์ฝ๋. if ์ง์ฅ์ ๋ถํ.
ํด๊ฒฐ: ๊ณตํต ๋ฉ์๋๋ฅผ ๋ถ๋ชจ์ ๋๊ธฐ:
public abstract class Animal {
public abstract void makeSound();
}
public void process(Animal animal) {
animal.makeSound(); // โ
๋คํ์ฑ!
}
โ instanceof ๊ฐ ์์ฃผ ๋ฑ์ฅํ๋ฉด ์ค๊ณ ์ฌ๊ฒํ ์ ํธ.
Animal animal = new Cat();
Dog dog = (Dog) animal; // โ ๏ธ ๋ฐํ์ ์๋ฌ: ClassCastException
dog.bark();
ํด๊ฒฐ: ์บ์คํ ์ instanceof ์ฒดํฌ (Unit 2.5์์ ์์ธํ):
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.bark();
}
๋๋ Java 16+ ํจํด ๋งค์นญ:
if (animal instanceof Dog dog) {
dog.bark();
}
public abstract class Animal {
public abstract void makeSound();
}
public class Dog extends Animal {
// makeSound() ๊ตฌํ ์ ํจ
}
// โ ์ปดํ์ผ ์๋ฌ
๊ท์น: ์ถ์ ๋ฉ์๋๋ ์์์ด ๋ฐ๋์ ๊ตฌํ (์์๋ abstract๋ฉด OK).
public class Animal {
public void eat(String food) { ... }
}
public class Dog extends Animal {
@Override
public void eat(int amount) { ... } // โ ์๊ทธ๋์ฒ ๋ค๋ฆ โ ์ค๋ฒ๋ก๋ฉ ๋จ
}
โ @Override ๊ฐ ์์ด์ ์ปดํ์ผ ์๋ฌ๋ก ์กํ. ์์ผ๋ฉด ์ค๋ฒ๋ก๋ฉ (๋ค๋ฅธ ๋ฉ์๋) ์ผ๋ก ์ธ์ โ ๋คํ์ฑ X.
public class Customer {
private Long id;
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false; // โ ๏ธ
return Objects.equals(id, ((Customer) obj).id);
}
}
public class VipCustomer extends Customer { ... }
Customer c1 = new Customer(1L);
Customer c2 = new VipCustomer(1L);
c1.equals(c2); // false โ getClass() ๋ค๋ฆ
๋ฌธ์ : ๋ถ๋ชจ-์์ ๋น๊ต ๋ถ๊ฐ.
ํด๊ฒฐ ์ ์ฃผ์: instanceof ์ฌ์ฉ vs getClass() โ Joshua Bloch๋ ์ผ๊ด์ฑ ์ํด getClass() ๊ถ์ฅ. ๊น์ ์ฃผ์ ์ด์ง๋ง, ์์ ๊ตฌ์กฐ + equals ๋ ์ ์คํ๊ฒ.
[Unit 2.3: ์์๊ณผ ์์ฑ์ ์ฒด์ด๋]
โ
[Unit 2.4: ๋คํ์ฑ] โ
โ
โ
โ ์ง๊ธ ์ฌ๊ธฐ
โ
[Unit 2.5: instanceof์ ํ๋ณํ] โ ๋คํ์ฑ์ ์์ ์ฅ์น
โ
[Unit 2.6: Nested/Inner/Anonymous]
1์ฃผ์ฐจ ๋ด:
๋ฏธ๋ ์ฃผ์ฐจ:
| ์์น | ๋คํ์ฑ๊ณผ์ ๊ด๊ณ |
|---|---|
| SRP | ์ฑ ์ ๋ถ๋ฆฌ ํ ๋คํ์ฑ์ผ๋ก ์กฐ๋ฆฝ |
| OCP | ๋คํ์ฑ์ ์ง์ ์ ๊ฒฐ๊ณผ โ ํ์ฅ ์ด๋ ค์๊ณ ์์ ๋ซํ์์ |
| LSP | ๋คํ์ฑ์ ์ ํํ ๊ท์น โ ์์์ด ๋ถ๋ชจ๋ฅผ ์์ ํ๊ฒ ๋์ฒด |
| ISP | ์ธํฐํ์ด์ค ๋ถ๋ฆฌ โ ๋คํ์ฑ ๋จ์ ์๊ฒ |
| DIP | ์ถ์ํ์ ์์กด โ ๋คํ์ฑ ์์ด ๋ถ๊ฐ๋ฅ |
โ SOLID 5์์น ์ค 4๊ฐ๊ฐ ๋คํ์ฑ์ ์ง์ ์์กด. Phase 3์์ ์์ธํ.
๊ฑฐ์ ๋ชจ๋ GoF ํจํด์ด ๋คํ์ฑ ๊ธฐ๋ฐ:
| ํจํด | ๋คํ์ฑ ํ์ฉ |
|---|---|
| Strategy | ์๊ณ ๋ฆฌ์ฆ์ ์ธํฐํ์ด์ค๋ก, ๊ตฌํ์ฒด ๊ต์ฒด |
| Template Method | ๋ถ๋ชจ๊ฐ ํ๋ฆ, ์์์ด ๊ตฌ์ฒด |
| Factory | ๊ฐ์ฒด ์์ฑ์ ์ถ์ํ |
| Decorator | ๊ฐ์ ์ธํฐํ์ด์ค๋ก ๊ธฐ๋ฅ ์ถ๊ฐ |
| Observer | ๋ค์ํ Observer๋ฅผ ํต์ผ๋ ์ธํฐํ์ด์ค๋ก |
| Command | ๋ช ๋ น์ ๊ฐ์ฒด๋ก ์ถ์ํ |
| State | ์ํ๋ณ ๋ค๋ฅธ ๋์์ ๋คํ์ฑ์ผ๋ก |
โ ๋คํ์ฑ์ ๋ชจ๋ฅด๋ฉด ๋์์ธ ํจํด ํ์ต ๋ถ๊ฐ.
| ์ง๋ฌธ | ์ด Unit์์์ ๋ต |
|---|---|
| "๋คํ์ฑ์ด๋?" | ๊ฐ์ ๋ฉ์๋ ํธ์ถ์ด ๊ฐ์ฒด์ ๋ฐ๋ผ ๋ค๋ฅด๊ฒ ๋์ |
| "๋คํ์ฑ์ ์ฅ์ ?" | ์ ์ข ๋ฅ ์ถ๊ฐ ์ ๊ธฐ์กด ์ฝ๋ ์ ๊ฑด๋๋ฆผ (OCP) |
| "๋์ ๋ฐ์ธ๋ฉ์ด๋?" | ๋ฐํ์์ ์ค์ ๊ฐ์ฒด ํ์ ์ ๋ฉ์๋๋ฅผ ํธ์ถ |
| "์ ์ ๋ฐ์ธ๋ฉ vs ๋์ ๋ฐ์ธ๋ฉ?" | static/private/final = ์ ์ , ์ธ์คํด์ค ๋ฉ์๋ = ๋์ |
| "VMT๊ฐ ๋ญ๊ฐ์?" | ํด๋์ค๋ณ ๋ฉ์๋ ํ ์ด๋ธ, JVM์ด ๋์ ๋ฐ์ธ๋ฉ์ ์ฌ์ฉ |
| "ํ๋๋ ๋คํ์ฑ?" | NO โ ํ๋๋ ๋ณ์ ํ์ ์ผ๋ก ๊ฒฐ์ |
| "์ค๋ฒ๋ก๋ฉ vs ์ค๋ฒ๋ผ์ด๋ฉ?" | ์ค๋ฒ๋ก๋ฉ=๊ฐ์ ํด๋์ค ๋ค๋ฅธ ์๊ทธ๋์ฒ, ์ค๋ฒ๋ผ์ด๋ฉ=์์์์ ์ฌ์ ์ |
1๏ธโฃ ๋คํ์ฑ์ "๊ฐ์ ๋ฉ์์ง์ ๊ฐ์ฒด๋ง๋ค ๋ค๋ฅธ ์๋ต" ์ด๋ค.
๋ถ๋ชจ ํ์ ๋ณ์๊ฐ ์์ ๊ฐ์ฒด๋ฅผ ๊ฐ๋ฆฌํฌ ์ ์๊ณ (
Animal a = new Dog()), ๋ฉ์๋ ํธ์ถ ์ JVM์ด ๋ฐํ์์ ์ค์ ๊ฐ์ฒด์ ๋ฉ์๋๋ฅผ ์ฐพ์ ์คํ (๋์ ๋ฐ์ธ๋ฉ). ์ด๊ฒ OOP 4๋ ์์น์ ์ ์ ์ด๋ฉฐ, ์๋ฐ ์ํ๊ณ (Spring, JPA, ๋์์ธ ํจํด) ์ ํ ๋.2๏ธโฃ VMT (Virtual Method Table) ๊ฐ ๋คํ์ฑ์ ๋น๋ฐ์ด๋ค.
๊ฐ ํด๋์ค๋ ์๊ธฐ๋ง์ VMT๋ฅผ ๊ฐ์ง๊ณ , ์ค๋ฒ๋ผ์ด๋๋ ๋ฉ์๋๋ ์์ ํด๋์ค์ VMT์ ๋ฑ๋ก๋จ. JVM์ ๋ฉ์๋ ํธ์ถ ์ ์ฐธ์กฐ ํ์ ์ด ์๋ ์ค์ ๊ฐ์ฒด์ VMT ๋ฅผ ์ฐธ์กฐ. ๋จ, static/private/final ๋ฉ์๋์ ํ๋๋ ์ ์ ๋ฐ์ธ๋ฉ โ ๋คํ์ฑ X.
3๏ธโฃ ๋คํ์ฑ์ OCP์ ์ง์ ์ ๊ตฌํ โ if ์ง์ฅ์ ์์ ๋ ๋ง๋ฒ์ด๋ค.
"์ ์ข ๋ฅ ์ถ๊ฐ ์ ๊ธฐ์กด ์ฝ๋๋ฅผ ์ ๊ฑด๋๋ฆฐ๋ค" ๋ OCP๊ฐ ๋คํ์ฑ์ผ๋ก ๊ฐ๋ฅ.
if (type.equals("VIP"))๊ฐ์ ๋ถ๊ธฐ๋ ๋คํ์ฑ์ผ๋ก ๋์ฒด.instanceof๊ฐ ์ฝ๋์ ๋ง์ด ๋ฑ์ฅํ๋ฉด ์ค๊ณ ์ฌ๊ฒํ ์ ํธ. SOLID, Spring DI, ๋์์ธ ํจํด ๋ชจ๋ ๋คํ์ฑ ์์ ์ ์์.
Animal a = new Dog() ๊ฐ ๊ฐ๋ฅํ ์ด์ ๋ฅผ ์ค๋ช
ํ ์ ์๋คQ1: Animal a = new Dog(); a.eat(); ์์ ํธ์ถ๋๋ eat()์ ๋๊ตฌ ๊ฒ์ธ๊ฐ?
Dog์ eat() ์ ๋๋ค (Dog๊ฐ eat()์ ์ค๋ฒ๋ผ์ด๋ํ ๊ฒฝ์ฐ).
์์ธ ์ค๋ช :
public class Animal {
public void eat() { System.out.println("๋๋ฌผ์ด ๋จน๋๋ค"); }
}
public class Dog extends Animal {
@Override
public void eat() { System.out.println("๊ฐ๊ฐ ๋จน๋๋ค"); }
}
Animal a = new Dog();
a.eat(); // "๊ฐ๊ฐ ๋จน๋๋ค"
์? โ ๋์ ๋ฐ์ธ๋ฉ (Dynamic Binding) ๋๋ฌธ.
JVM ๋ด๋ถ ํ๋ฆ:
1. a.eat() ํธ์ถ
2. JVM: "a ๋ณ์์ ์ปดํ์ผ ํ์ ํ์
์ Animal์ด์ง๋ง..."
3. JVM: "a๊ฐ ๊ฐ๋ฆฌํค๋ ์ค์ ๊ฐ์ฒด๋ ๋ฌด์์ธ๊ฐ? โ Dog ์ธ์คํด์ค"
4. JVM: "Dog์ VMT์์ eat() ๋ฉ์๋ ์ฐพ๊ธฐ"
5. VMT[eat] = Dog.eat (์ค๋ฒ๋ผ์ด๋๋์ด ์์)
6. Dog.eat() ์คํ โ "๊ฐ๊ฐ ๋จน๋๋ค"
ํต์ฌ ํต์ฐฐ โญ :
"์ฐธ์กฐ ๋ณ์์ ํ์ (Animal)์ด ๋ฌด์์ธ์ง๋ ์ค์ํ์ง ์๋ค. ์ค์ ๊ฐ์ฒด์ ํ์ (Dog)์ด ๋ฉ์๋๋ฅผ ๊ฒฐ์ ํ๋ค."
์์ธ ์ํฉ:
๋ง์ฝ Dog๊ฐ eat()์ ์ค๋ฒ๋ผ์ด๋ํ์ง ์์๋ค๋ฉด?
public class Dog extends Animal {
// eat() ์ค๋ฒ๋ผ์ด๋ X
}
Animal a = new Dog();
a.eat(); // "๋๋ฌผ์ด ๋จน๋๋ค" โ ์์๋ฐ์ Animal.eat() ํธ์ถ
โ ์์์ด ์ ๊ฐ์ง๋ฉด ๋ถ๋ชจ์ ๊ฒ์ ์ฌ์ฉ (VMT์ ์์ฐ์ค๋ฌ์ด ๋์).
Q2: ์ปดํ์ผ ํ์ ํ์ ๊ณผ ๋ฐํ์ ํ์ ์ ์ฐจ์ด๋?
์ปดํ์ผ ํ์ ํ์
= ๋ณ์ ์ ์ธ ์ ๋ช
์ํ ํ์
(์ฐธ์กฐ ํ์
)
๋ฐํ์ ํ์
= ๋ณ์๊ฐ ์ค์ ๋ก ๊ฐ๋ฆฌํค๋ ๊ฐ์ฒด์ ํ์
Animal a = new Dog();
// โ โ
// ์ปดํ์ผ ํ์ ๋ฐํ์ ํ์
// ํ์
(์ฐธ์กฐ) (์ค์ ๊ฐ์ฒด)
๊ฐ๊ฐ์ด ๊ฒฐ์ ํ๋ ๊ฒ:
1. ์ฌ์ฉ ๊ฐ๋ฅํ ๋ฉ์๋ โญ :
Animal a = new Dog();
a.eat(); // โ
Animal์ ์์
a.bark(); // โ ์ปดํ์ผ ์๋ฌ โ Animal์๋ bark() ์์
โ ์ปดํ์ผ๋ฌ๋ ๋ณ์์ ์ ์ธ ํ์ ๋ง ๋ด.
2. ์บ์คํ ๊ฐ๋ฅ ์ฌ๋ถ:
Animal a = new Dog();
Dog d = (Dog) a; // โ
๋ช
์์ ์บ์คํ
ํ์
String s = (String) a; // โ ์ปดํ์ผ ์๋ฌ โ ๋ฌด๊ดํ ํ์
1. ์ค์ ํธ์ถ๋๋ ๋ฉ์๋ (๋คํ์ฑ) โญ :
Animal a = new Dog();
a.eat(); // Dog.eat() ํธ์ถ โ ๋ฐํ์ ํ์
์ผ๋ก ๊ฒฐ์
2. instanceof ๊ฒฐ๊ณผ:
Animal a = new Dog();
a instanceof Dog; // true โ ๋ฐํ์ ํ์
a instanceof Animal; // true โ ๋ฐํ์ ํ์
์ ๋ถ๋ชจ
a instanceof Cat; // false
3. ClassCastException ๋ฐ์ ์ฌ๋ถ:
Animal a = new Cat();
Dog d = (Dog) a; // ์ปดํ์ผ OK, ๋ฐํ์์ ClassCastException โ ๏ธ
โ ์ปดํ์ผ๋ฌ๋ "Dog๋ก ์บ์คํ ํ๊ฒ ๋ค" ๋ง ํ์ธ, ์ค์ ๊ฐ๋ฅ ์ฌ๋ถ๋ ๋ฐํ์.
๋น์ ๋ก ์ ๋ฆฌ:
์ปดํ์ผ ํ์ ํ์
= ๋ช
ํจ์ ์ง์ฑ
- "๋๋ ๋งค๋์ ์
๋๋ค"
- ๋ช
ํจ๋ง ๋ณด๋ฉด ์ ์ ์๋ ์ ๋ณด
๋ฐํ์ ํ์
= ์ค์ ์ฌ๋
- ๊ทธ ์ฌ๋์ ์ง์ง ๋ฅ๋ ฅ
- ์ค์ ๋ก ๋ง๋์ผ ์ ์ ์์
Animal a = new Dog();
๋ช
ํจ: "๋๋ฌผ (Animal)"
์ค์ : ๊ฐ (Dog)
ํต์ฌ ์ฐจ์ด ํ:
| ์ธก๋ฉด | ์ปดํ์ผ ํ์ ํ์ | ๋ฐํ์ ํ์ |
|---|---|---|
| ๊ฒฐ์ ์์ | ์ปดํ์ผ ์ | ์คํ ์ |
| ๋๊ฐ ๋ด | ์ปดํ์ผ๋ฌ | JVM |
| ๊ฒฐ์ ํ๋ ๊ฒ | ์ฌ์ฉ ๊ฐ๋ฅํ ๋ฉค๋ฒ | ์ค์ ๋ฉ์๋ |
| ๋ณ๊ฒฝ ๊ฐ๋ฅ? | NO (์ ์ธ ํ ๊ณ ์ ) | NO (๊ฐ์ฒด ์์ฑ ํ ๊ณ ์ ) |