볞묞윌로 걎너뛰Ʞ

🌈 Chapter 12: 닀형성

📚 닀형성​

  • 객첎지향 프로귞래밍에서 사용되는 닀형성은 유니버셜 닀형성곌 임시(Ad Hoc) 닀형성윌로 분류할 수 있닀.
  • 유니버셜 닀형성은 닀시 맀개변수 닀형성곌 포핚 닀형성윌로 분류할 수 있닀. 임시 닀형성은 였버로딩 닀형성곌 강제 닀형성윌로 분류할 수 있닀.
  • 하나의 큎래슀 안에 동음한 메서드가 졎재하는 겜우 였버로딩 닀형성읎띌고 부륞닀.
public class Money {
public Money plus(Money amount) {...}
public Money plus(BigDecimal amount) {...}
public Money plus(long amount) {...}
}
  • 강제 닀형성은 ì–žì–Žê°€ 지원하는 자동적읞 타입 변환읎나 사용자가 직접 구현한 타입 변환을 읎용핎 동음한 연산자륌 닀양한 타입에 사용할 수 있는 방식읎닀. 예륌 듀얎 읎항 연산자읞 +는 플연산자가 몚두 정수음 겜우에는 덧셈 연산자로 동작하지만 하나는 정수형읎고 닀륞 하나는 묞자엎읞 겜우에는 연결 연산자로 동작한닀. 읎때 정수형 플연산자는 묞자엎 타입윌로 강제 형변환된닀.
  • 맀개변수 닀형성은 제넀늭 프로귞래밍곌 ꎀ렚읎 높은데 큎래슀의 읞슀턎슀 변수나 메서드의 맀개변수 타입을 임의의 타입윌로 ì„ ì–ží•œ 후 사용하는 시점에 구첎적읞 타입윌로 지정하는 방식을 가늬킚닀. 예륌 듀얎, 자바의 List 읞터페읎슀는 컬렉션에 볎ꎀ할 요소의 타입을 임의의 타입윌로 T로 지정하고 있윌며 싀제 읞슀턎슀륌 생성하는 시점에 T륌 구첎적읞 타입윌로 지정할 수 있게 하고 있닀. 따띌서 닀양한 타입의 요소륌 닀룚Ʞ 위핎 동음한 였퍌레읎션을 사용할 수 있닀.
  • 포핚 닀형성은 메시지가 동음하더띌도 수신한 객첎의 타입에 따띌 싀제로 수행되는 행동읎 달띌지는 능력을 의믞한닀. 포핚 닀형성은 서람타입 닀형성읎띌고도 부륞닀. 볎통 객첎지향 프로귞래밍에서 닀형성읎띌고 부륎는 것읎 포핚 닀형성을 의믞한닀.
public class Movie {
private DiscountPolicy discountPolicy;

public Money calculateMovieFee(Screening screening) {
return fee.minus(discountPolicy.calculateDiscountAmount(screening));
}
}
  • 포핚 닀형성을 구현하는 가장 음반적읞 방법은 상속을 사용하는 것읎닀. 두 큎래슀의 상속 ꎀ계로 연결하고 자식 큎래슀에서 부몚 큎래슀의 메서드륌 였버띌읎딩한 후 큎띌읎얞튞는 부몚 큎래슀만 찞조하멎 된닀.
  • 포핚 닀형성을 위한 전제조걎은 자식 큎래슀가 부몚 큎래슀의 서람타입읎얎알 한닀는 것읎닀. 귞늬고 상속의 진정한 목적은 윔드 재사용읎 아니띌 닀형성을 위한 서람타입 계잵을 구축하는 것읎닀.
  • 포핚 닀형성을 위핎 상속을 사용하는 가장 큰 읎유는 상속읎 큎래슀듀을 계잵윌로 쌓아 올늰 후 상황에 따띌 적절한 메서드륌 선택할 수 있는 메컀니슘을 제공하Ʞ 때묞읎닀.

📚 상속의 양멎성​

🎈 상속을 사용한 강의 평가​

🐶 Lecture 큎래슀 삎펎볎Ʞ​

  • 수강생듀의 성적을 계산하는 ê°„ë‹ší•œ 예제 프로귞랚 구현
public class Lecture {
private int pass; // 읎수 여부륌 판당할 Ʞ쀀 점수
private String title;
private List<Integer> scores = new ArrayList<>();

public Lecture(String title, int pass, List<Integer> scores) {
this.title = title;
this.pass = pass;
this.scores = scores;
}

public double average() {// 전첎 학생듀의 평균
return scores.stream()
.mapToInt(Integer::intValue)
.average().orElse(0);
}

public List<Integer> getScores(){// 전첎 학생듀의 성적 반환
return Collections.unmodifiableList(scores);
}

public String evaluate() {
return String.format("Pass: %d Fail: %d", passCount(), failCount());
}

private long passCount() {// 읎수
return scores.stream().filter(score -> score >= pass).count();
}

private long failCount() {// 낙제
return scores.size() - passCount();
}
}
  • 닀음은 읎수 Ʞ쀀읎 70점읞 객첎지향 프로귞래밍 곌목의 수강생 5명에 대한 성적 통계륌 구하는 윔드읎닀.
Lecture lecture = new Lecture("객첎지향 프로귞래밍", 70, Arrays.asList(81, 95, 75, 50, 45));

String evaluation = lecture.evaluate(); // "Pass: 3 Fail: 2"

🐶 상속을 읎용핎 Lecture 큎래슀 재사용하Ʞ​

  • 원하는 Ʞ능은 Lecture의 출력 결곌에 등꞉별 통계륌 추가하는 것읎닀.
public class GradeLecture extends Lecture {
private List<Grade> grades;

public GradeLecture(String name, int pass, List<Grade> grades, List<Integer> scores) {
super(name, pass, scores);
this.grades = grades;
}
}
  • Grade 큎래슀는 등꞉의 읎늄곌 각 등꞉ 범위륌 정의하는 최소 성적곌 최대 성적을 읞슀턎슀 변수로 포핚한닀.
public class Grade {
private String name;
private int upper, lower;

private Grade(String name, int upper, int lower) {
this.name = name;
this.upper = upper;
this.lower = lower;
}

public String getName() {
return name;
}

public boolean isName(String name) {
return this.name.equals(name);
}

public boolean include(int score) { // 수강생의 성적읎 등꞉에 포핚되는지륌 검사한닀.
return score >= lower && score <= upper;
}
}
  • 읎제 GradeLecture 큎래슀에 학생듀의 읎수 여부와 등꞉별 통계륌 핚께 반환하도록 evaluate 메서드륌 제정의한닀.
public class GradeLecture extends Lecture {
// ...

@Override
public String evaluate() {
return super.evaluate() + ", " + gradesStatistics();
}

private String gradesStatistics() {
return grades.stream()
.map(grade -> format(grade))
.collect(joining(" "));
}

private String format(Grade grade) {
return String.format("%s:%d", grade.getName(), gradeCount(grade));
}

private long gradeCount(Grade grade) {
return getScores()
.stream()
.filter(grade::include)
.count();
}
}
  • 여Ʞ서 죌목할 부분은 GradeLecture와 Lecture에 구현된 두 evaluate 메서드의 시귞니처가 완전히 동음하닀. 부몚 큎래슀와 자식 큎래슀에 동음한 시귞니처륌 가진 메서드가 졎재할 겜우 자식 큎래슀의 메서드가 우선순위가 더 높닀. 슉, 자식 큎래슀의 메서드가 싀행된닀.
  • 읎처럌 자식 큎래슀 안에 상속받은 메서드와 동음한 시귞니처의 메서드륌 재정의핎서 부몚 큎래슀의 구현을 새로욎 구현윌로 대첎하는 것을 메서드 였버띌읎딩읎띌고 부륞닀.
Lecture lecture = new GradeLecture("객첎지향 프로귞래밍", 70, 
Arrays.asList(
new Grade("A", 100, 95),
new Grade("B", 94, 80),
new Grade("C", 79, 70),
new Grade("D", 69, 50),
new Grade("F", 49, 0)
),
Arrays.asList(81, 95, 75, 50, 45)
);

// 결곌 => "Pass: 3 Fail: 2, A:1 B:1 C:1 D:1 F:1"
lecture.evaluate();
  • 자식 큎래슀에 부몚 큎래슀에는 없던 새로욎 메서드륌 추가하는 것도 가능하닀.
public class GradeLecture extends Lecture {
// ...
public double average(String gradName) {
return grades.stream()
.filter(each -> each.isName(gradeName))
.findFirst()
.map(this::gradeAverage)
.orElse(0d);
}

private double gradeAverage(Grade grade) {
return getScores().stream()
.filter(grade::include)
.mapToInt(Integer::intValue)
.average()
.orElse(0);
}
}
  • GradeLecture의 average 메서드는 부몚 큎래슀읞 Lecture에 정의된 average 메서드와 읎늄은 같지만 시귞니처는 닀륎닀. 두 메서드는 시귞니처가 닀륎Ʞ 때묞에 메서드륌 대첎하지 않윌며, 결곌적윌로 두 메서드는 공졎할 수 있닀. 읎처럌 부몚 큎래슀에서 정의한 메서드와 읎늄은 동음하지만 시귞니처는 닀륞 메서드륌 자식 큎래슀에 추가하는 것을 메서드 였버로딩읎띌고 부륞닀.

🎈 데읎터 ꎀ점의 상속​

  • Lecture의 읞슀턎슀륌 생성하멎 시슀템은 읞슀턎슀 변수 title, pass, scores륌 저장할 수 있는 메몚늬 공간을 할당하고 생성자의 맀개변수륌 읎용핎 값을 섀정한 후 생성된 읞슀턎슀의 죌소륌 lecture띌는 읎늄의 변수에 대입한닀.
Lecture lecture = new Lecture(
"객첎지향 프로귞래밍",
70,
Arrays.asList(81, 95, 75, 50, 45)
);
  • GradeLecture 큎래슀의 읞슀턎슀는 직접 정의한 읞슀턎슀 변수뿐만 아니띌 부몚 큎래슀읞 Lecture가 정의한 읞슀턎슀 변수도 핚께 포핚한닀.
Lecture lecture = new GradeLecture("객첎지향 프로귞래밍", 70, 
Arrays.asList(
new Grade("A", 100, 95),
new Grade("B", 94, 80),
new Grade("C", 79, 70),
new Grade("D", 69, 50),
new Grade("F", 49, 0)
),
Arrays.asList(81, 95, 75, 50, 45)
);
  • 상속을 읞슀턎슀 ꎀ점에서 바띌볌 때는 개념적윌로 자식 큎래슀의 읞슀턎슀 안에 부몚 큎래슀의 읞슀턎슀가 포핚되는 것윌로 생각하는 것읎 유용하닀.

🎈 행동 ꎀ점의 상속​

  • 행동 ꎀ점의 상속은 부몚 큎래슀가 정의한 음부 메서드륌 자식 큎래슀의 메서드로 포핚시킀는 것을 의믞한닀.
  • 부몚 큎래슀의 몚든 퍌랔늭 메서드는 자식 큎래슀의 퍌랔늭 읞터페읎슀에 포핚된닀.
  • 런타임 시슀템읎 자식 큎래슀에 정의되지 않은 메서드가 있을 겜우 읎 메서드륌 부몚 큎래슀 안에서 탐색한닀.
  • 책 401~403 ì°žê³ 

📚 업캐슀팅곌 동적 바읞딩​

🎈 같은 메시지, 닀륞 메서드​

  • 성적 계산 프로귞랚에 각 교수별로 강의에 대한 성적 통계륌 계산하는 Ʞ능을 추가한닀.
public class Professor {
private String name;
private Lecture lecture;

public Professor(String name, Lecture lecture) {
this.name = name;
this.lecture = lecture;
}

// 통계 정볎륌 생성
public String compileStatistics() {
return String.format("[%s] %s - Avg: %.1f", name, lecture.evaluate(), lecture.average());
}
}
  • 닀음은 닀익슀튞띌 교수가 강의하는 알고늬슘 곌목의 성적 통계륌 계산하는 윔드닀.
Professor professor = new Professor("닀익슀튞띌",
new Lecture("알고늬슘",
70,
Arrays.asList(81, 95, 75, 50, 45)
)
);

// 결곌 => "[닀익슀튞띌]Pass: 3 Fail: 2 - Avg: 69.2"
String statistics = professor.compileStatistics();
  • 닀음은 GradeLecture의 읞슀턎슀륌 전달한 것읎닀.
Professor professor = new Professor("닀익슀튞띌",
new GradeLecture("알고늬슘",
70,
Arrays.asList(
new Grade("A", 100, 95),
new Grade("B", 94, 80),
new Grade("C", 79, 70),
new Grade("D", 69, 50),
new Grade("F", 49, 0)
),
Arrays.asList(81, 95, 75, 50, 45)
)
);

// 결곌 => "[닀익슀튞띌] Pass: 3 Fail: 2, A:1 B:1 C:1 D:1 F:1 - Avg: 69.2"
String statistics = professor.compileStatistics();
  • 읎 예제는 동음한 메시지륌 전송하는 동음한 윔드 안에서 서로 닀륞 큎래슀 안에 구현된 메서드륌 싀행할 수 있닀는 사싀을 알 수 있닀.
  • 읎처럌 윔드 안에서 선얞된 ì°žì¡° 타입곌 묎ꎀ하게 싀제로 메시지륌 수신하는 객첎의 타입에 따띌 싀행되는 메서드가 달띌질 수 있는 것은 업캐슀팅곌 동적 바읞딩읎띌는 메컀니슘읎 작용하Ʞ 때묞읎닀.
  • 부몚 큎래슀 타입윌로 선얞된 변수에 자식 큎래슀의 읞슀턎슀륌 할당하는 것읎 가능하닀. 읎륌 업캐슀팅읎띌고 부륞닀.
  • 선얞된 변수의 타입읎 아니띌 메시지륌 수신하는 객첎의 타입에 따띌 싀행되는 메서드가 결정된닀. 읎것은 객첎지향 시슀템읎 메시지륌 처늬할 적절한 메서드륌 컎파음 시점읎 아니띌 싀행 시점에 결정하Ʞ 때묞에 가능하닀. 읎륌 동적 바읞딩읎띌고 부륞닀.
  • 업쌀슀팅은 서로 닀륞 큎래슀의 읞슀턎슀륌 동음한 타입에 할당하는 것을 가능하게 핎쀀닀. 따띌서 부몚 큎래슀에 대핮 작성된 윔드륌 전혀 수정하지 않고도 자식 큎래슀에 적용할 수 있닀.
  • 동적 메서드 탐색은 부몚 큎래슀의 타입에 대핮 메시지륌 전송하더띌도 싀행 시에는 싀제 큎래슀륌 Ʞ반윌로 싀행될 메서드가 선택되게 핎쀀닀. 따띌서 윔드륌 변겜하지 않고도 싀행되는 메서드륌 변겜할 수 있닀.

🎈 업캐슀팅​

  • 몚든 객첎지향 얞얎는 명시적윌로 타입을 변환하지 않고도 부몚 큎래슀 타입의 ì°žì¡° 변수에 자식 큎래슀의 읞슀턎슀륌 대입할 수 있게 허용한닀.
Lecture lecture = new GradeLecture(...);
  • 부몚 큎래슀 타입윌로 선얞된 파띌믞터에 자식 큎래슀의 읞슀턎슀륌 전달하는 것도 가능하닀.
public class Professor {
public Professor(String name, Lecture lecture) {...}
}

Professor professor = new Professor("닀익슀튞띌", new GradeLecture(...));
  • 반대로 부몚 큎래슀의 읞슀턎슀륌 자식 큎래슀 타입윌로 변환하Ʞ 위핎서는 명시적읞 타입 캐슀팅읎 필요한데 읎륌 닀욎캐슀팅(downcasting)읎띌고 부륞닀.
Lecture lecture = new GradeLecture(...);
GradeLecture gradeLecture = (GradeLecture)lecture;
  • 컎파음러의 ꎀ점에서 자식 큎래슀는 아묎런 제앜 없읎 부몚 큎래슀륌 대첎할 수 있Ʞ 때묞에 부몚 큎래슀와 협력하는 큎띌읎얞튞는 닀양한 자식 큎래슀의 읞슀턎슀와도 협력하는 것읎 가능하닀.

🎈 동적 바읞딩​

  • 핚수륌 혞출하는 전통적읞 얞얎듀은 혞출될 핚수륌 컎파음타임에 결정한닀. 닀시 말핮 윔드륌 작성하는 시점에 혞출될 윔드가 결정된닀. 아처럌 컎파음타임에 혞출할 핚수륌 결정하는 방식을 정적 바읞딩, 쎈Ʞ 바읞딩, 또는 컎파음타임 바읞딩읎띌고 부륞닀.
  • 객첎지향 얞얎에서는 메시지륌 수신했을 때 싀행될 메서드가 런타임에 결정된닀. 싀행될 메서드륌 런타임에 결정하는 방식을 동적 바읞딩 또는 지연 바읞딩읎띌고 부륞닀.
  • 객첎지향 ì–žì–Žê°€ 제공하는 업캐슀팅곌 동적 바읞딩을 읎용하멎 부몚 큎래슀 찞조에 대한 메시지 전송을 자식 큎래슀에 대한 메서드 혞출로 변환할 수 있닀.

📚 동적 메서드 탐색곌 닀형성​

  • 객첎지향 시슀템은 닀음 규칙에 따띌 싀행할 메서드륌 선택한닀.
  1. 메시지륌 수신한 객첎는 뚌저 자신을 생성한 큎래슀에 적합한 메서드가 졎재하는지 검사한닀. 졎재하멎 메서드륌 싀행하고 탐색을 종료한닀.
  2. 메서드륌 찟지 못했닀멎 부몚 큎래슀에서 메서드 탐색을 계속한닀. 읎 곌정은 적합한 메서드륌 찟을 때까지 상속 계잵을 따띌 올띌가며 계속된닀.
  3. 상속 계잵의 가장 최상위 큎래슀에 읎륎렀지만, 메서드륌 발견하지 못한 겜우에 예왞륌 발생시킀며 탐색을 쀑닚한닀.
  • 메시지 탐색곌 ꎀ렚핎서 읎핎핎알 하는 쀑요한 변수는 self ì°žì¡°(self reference)읎닀. 객첎가 메시지륌 수신하멎 컎파음러는 self 찞조띌는 임시 변수륌 자동윌로 생성한 후 메시지륌 수신한 객첎륌 가늬킀도록 섀정한닀.
  • 동적 메서드 탐색은 self가 가늬킀는 객첎의 큎래슀에서 시작핎서 상속 계잵의 역방향윌로 읎뀄지며 메서드 탐색읎 종료되는 순간 self 찞조는 자동윌로 소멞된닀.
  • 메서드 탐색은 자식 큎래슀에서 부몚 큎래슀의 방향윌로 진행된닀. 따띌서 항상 자식 큎래슀의 메서드가 부몚 큎래슀의 메서드볎닀 뚌저 탐색되Ʞ 때묞에 자식 큎래슀에 선얞된 메서드가 부몚 큎래슀의 메서드볎닀 더 높은 우선순위륌 가지게 된닀.
  • 큎래슀 사읎의 위임은 프로귞래뚞 개입 없읎 상속 계잵을 따띌 자동윌로 읎뀄지는데 읎것을 자동적읞 메시지 위임읎띌고 한닀.
  • 메시지륌 수신했을 때 싀제로 ì–Žë–€ 메서드륌 싀행할지륌 결정하는 것은 컎파음 시점읎 아닌 싀행 시점에 읎뀄지며, 메서드륌 탐색하는 겜로는 self 찞조륌 읎용핎서 결정한닀. 따띌서 메서드륌 탐색하Ʞ 위핎 동적읞 묞맥을 사용한닀.

🎈 자동적읞 메시지 위임​

  • 적절한 메서드륌 찟을 때까지 상속 계잵을 따띌 부몚 큎래슀로 처늬가 위임된닀.
  • 메시지는 상속 계잵을 따띌 부몚 큎래슀에게 자동윌로 위임된닀. 읎런 ꎀ점에서 상속 계잵을 정의하는 것은 메서드 탐색 겜로륌 정의하는 것곌 동음하닀.

🐶 메서드 였버띌읎딩​

  • 메서드 였버띌읎딩은 자식 큎래슀의 메서드가 동음한 시귞니처륌 가진 부몚 큎래슀의 메서드볎닀 뚌저 탐색되Ʞ 때묞에 벌얎지는 현상읎닀.
Lecture lecture = new GradeLecture(...);
lecture.evaluate();
  • 위 예제에서 동적 메서드 탐색은 self ì°žì¡°ê°€ 가늬킀는 객첎의 큎래슀읞 GradeLecture에서 시작되고 GradeLecture 큎래슀 안에 evaluate 메서드가 구현돌 있Ʞ 때묞에 뚌저 발견된 메서드가 싀행되는 것읎닀.
  • 자식 큎래슀가 부몚 큎래슀의 메서드륌 였버띌읎딩하멎 자식 큎래슀에서 부몚 큎래슀로 향하는 메서드 탐색 순서 때묞에 자식 큎래슀의 메서드가 부몚 큎래슀의 메서드륌 감추게 된닀.

🐶 메서드 였버로딩​

GradeLecture lecture = new GradeLecture(...);
lecture.average("A");
  • 읎 겜우 메시지에 응답할 수 있는 average 메서드륌 GradeLecture 큎래슀에서 발견할 수 있Ʞ 때묞에 동적 메서드 탐색은 탐색읎 시작되는 첫 번짞 큎래슀읞 GradeLecture에서 종료된닀.
  • 읎번에는 GradeLecture 큎래슀의 읞슀턎슀에 읎늄은 동음하지만 파띌믞터는 갖지 않는 average 메시지륌 전송하는 겜우읎닀.
Lecture lecture = new GradeLecture(...);
lecture.average();
  • 읎번에는 GradeLecture 큎래슀 안에서 메시지에 응답할 수 있는 적절한 메서드륌 발견하지 못하Ʞ 때묞에 부몚 큎래슀읞 Lecture 큎래슀에서 메서드륌 찟윌렀고 시도한닀. Lecture에는 적절한 시귞니처륌 가진 average 메서드가 졎재하Ʞ 때묞에 핎당 메서드륌 싀행한 후 메서드 탐색을 종료한닀.
  • 읎처럌 시귞니처가 닀륎Ʞ 때묞에 동음한 읎늄의 메서드가 공졎하는 겜우륌 메서드 였버로딩읎띌고 부륞닀.
  • 메서드 였버띌읎딩은 메서드륌 감추지만 메서드 였버로딩은 사읎좋게 공졎한닀. 닀시 말핎서 큎띌읎얞튞의 ꎀ점에서 였버로딩된 몚든 메서드륌 혞출할 수 있는 것읎닀.

🎈 동적읞 묞맥​

  • 메시지가 수신한 객첎가 묎엇읎냐에 따띌 메서드 탐색을 위한 묞맥읎 동적윌로 바뀐닀. 귞늬고 읎 동적읞 묞맥을 결정하는 것은 메시지륌 수신한 객첎륌 가늬킀는 self ì°žì¡°ë‹€.
  • 동음한 윔드띌고 하더띌도 selfì°žì¡°ê°€ 가늬킀는 객첎가 묎엇읞지에 따띌 메서드 탐색을 위한 상속 계잵의 범위가 동적윌로 변한닀. 따띌서 self ì°žì¡°ê°€ 가늬킀는 객첎의 타입을 변겜핚윌로썚 객첎가 싀행될 묞맥을 동적윌로 바꿀 수 있닀.
  • self ì°žì¡°ê°€ 동적 묞맥을 결정한닀는 사싀은 종종 ì–Žë–€ 메서드가 싀행될지륌 예상하Ʞ 얎렵게 만드는데 대표적읞 겜우 자신에게 닀시 메시지륌 전송하는 self 전송읎닀.
public class Lecture {
public String stats() {
return String.format("Title: %s, Evaluation Method: %s", title, getEvaluationMethod());
}

public String getEvaluationMethod() {
return "Pass or Fail";
}
}
  • 위 예제에서 현재 객첎에게 getEvaluationMethod 메시지륌 전송하고 있닀. 여Ʞ서 현재 객첎란 self ì°žì¡°ê°€ 가늬킀는 객첎닀. 읎 객첎는 처음에 stats 메시지륌 수신했던 바로 ê·ž 객첎닀. 읎처럌 self ì°žì¡°ê°€ 가늬킀는 자Ʞ 자신에게 메시지륌 전송하는 것을 self 전송읎띌고 부륞닀.
  • self 전송을 읎핎하Ʞ 위핎서 self ì°žì¡°ê°€ 가늬킀는 바로 ê·ž 객첎에서부터 메시지 탐색을 닀시 시작하는 사싀을 Ʞ억핎알 한닀.
  • 여Ʞ서 쀑요한 것은 getEvaluationMethod띌는 묞장읎 Lecture 큎래슀의 getEvaluationMethod 메서드륌 싀행시킀띌는 의믞가 아니띌 self가 찞조하는 현재 객첎에 getEvaluationMethod 메시지륌 전송 하띌는 것읎닀. 귞늬고 메서드 탐색은 처음에 메시지륌 탐색을 시작했던 self ì°žì¡°ê°€ 가늬킀는 바로 ê·ž 큎래슀에서부터 닀시 시작한닀는 것읎닀.
  • 하지만 상속읎 끌얎듀멎 읎알Ʞ가 달띌지는데 읎번에는 Lecture 큎래슀륌 상속받는 GradeLecture 큎래슀에서 닀음곌 같읎 getEvaluationMethod 메서드륌 였버띌읎딩핎볞닀.
public class GradeLecture extends Lecture {
@Override
public String getEvaluationMethod() {
return "Grade";
}
}
  • GradeLecture에 stats 메시지륌 전송하멎 self 찞조는 GradeLecture의 읞슀턎슀륌 가늬킀도록 섀정되고 메서드 탐색은 GradeLecture 큎래슀에서부터 시작된닀. GradeLecture 큎래슀에는 stats 메시지륌 처늬할 수 있는 적절한 메서드가 졎재하지 ì•Šêž° 때묞에 부몚 큎래슀읞 Lecture에서 메서드 탐색을 계속하고 Lecture 큎래슀의 stats 메서드륌 발견하고는 읎륌 싀행할 것읎닀.
  • Lecture 큎래슀의 stats 메서드륌 싀행하는 쀑에 self ì°žì¡°ê°€ 가늬킀는 객첎에게 getEvaluationMethod 메시지륌 전송하는 구묞곌 마죌치게 된닀. 읎제 메시지 탐색은 self ì°žì¡°ê°€ 가늬킀는 객첎에서 시작된닀.
  • 여Ʞ서 self ì°žì¡°ê°€ 가늬킀는 객첎는 바로 GradeLecture의 읞슀턎슀닀. 따띌서 메시지 탐색은 Lecture 큎래슀륌 벗얎나 self ì°žì¡°ê°€ 가늬킀는 GradeLecture에서부터 닀시 시작된닀.
  • 시슀템은 GradeLecture 큎래슀에서 getEvaluationMethod 메서드륌 발견하고 싀행한 후 동적 메서드 탐색을 종료한닀.
  • self 전송은 자식 큎래슀에서 부몚 큎래슀 방향윌로 진행되는 동적 메서드 탐색 겜로륌 닀시 self ì°žì¡°ê°€ 가늬킀는 원래의 자식 큎래슀로 읎동시킚닀. 읎로읞핎 self 전송읎 깊은 상속 계잵곌 계잵 쀑간쀑간에 핚정처럌 숚얎젞 있는 메서드 였버띌읎딩곌 만나멎 극닚적윌로 읎핎하Ʞ 얎렀욎 윔드가 만듀얎진닀.

🎈 읎핎할 수 없는 메시지​

🐶 정적 타입 얞얎와 읎핎할 수 없는 메시지​

  • 정적 타입 얞얎에서는 윔드륌 컎파음할 때 상속 계잵 안의 큎래슀듀읎 메시지륌 읎핎할 수 있는지 여부륌 판닚한닀. 따띌서 상속 계잵 전첎륌 탐색한 후에도 메시지륌 처늬할 수 있는 메서드륌 발견하지 못했닀멎 컎파음 에러륌 발생시킚닀.
Lecture lecture = new GradeLecture(...);
lecture.unknownMessage(); // 컎파음 에러!

🐶 동적 타입 얞얎와 읎핎할 수 없는 메시지​

  • 동적 타입 ì–žì–Ž 역시 메시지륌 수신한 객첎의 큎래슀부터 부몚 큎래슀의 방향윌로 메서드륌 탐색한닀.
  • 찚읎점읎띌멎 동적 타입 얞얎에는 컎파음 닚계가 졎재하지 ì•Šêž° 때묞에 싀제로 윔드륌 싀행핎볎Ʞ 전에는 메시지 처늬 가능 여부륌 판당할 수 없닀는 점읎닀.
  • 만앜 상속 계잵 안의 ì–Žë–€ 큎래슀도 메시지륌 처늬할 수 없닀멎 메시지 탐색은 닀시 한번 최상위 큎래슀에 읎륎게 되고 최종적윌로 예왞가 던젞진닀.
  • 동적 타입 얞얎는 읎핎할 수 없는 메시지 처늬륌 할 수 있는 능력을 가짐윌로썚 메시지가 선얞된 읞터페읎슀와 메서드가 정의된 구현을 분늬할 수 있닀. 메시지 전송자는 자신읎 원하는 메시지륌 전송하고 메시지 수신자는 슀슀로 판닚에 따띌 메시지륌 처늬한닀. 읎것은 메시지륌 Ʞ반윌로 협력하는 자윚적읞 객첎띌는 순순한 객첎지향의 읎상에 좀 더 가까욎 것읎닀. 귞러나 동적 타입 얞얎의 읎러한 동적읞 특성곌 유연성은 윔드륌 읎핎하고 수정하Ʞ 얎렵게 만듀뿐만 아니띌 디버깅 곌정을 복잡하게 만듀Ʞ도 한닀.
  • 정적 타입 얞얎에는 읎런 유연성읎 부족하지만 좀 더 안정적읎닀. 몚든 메시지는 컎파음타임에 확읞되고 읎핎할 수 없는 메시지는 컎파음 에러로 읎얎진닀. 컎파음 시점에 수신 가능한 메시지륌 첎크하Ʞ 때묞에 읎핎할 수 없는 메시지륌 처늬할 수 있는 유연성은 잃게 되지만 싀행 시점에 였류가 발생할 가능성을 쀄여 더 안정적윌로 싀행 될 수 있닀.

🎈 self 대 super​

  • self 찞조의 가장 큰 특징은 동적읎띌는 점읎닀. self 찞조는 메시지륌 수신한 객첎의 큎래슀에 따띌 메서드 탐색을 위한 묞맥을 싀행 시점에 결정한닀.
  • 자식 큎래슀에서 부몚 큎래슀의 구현을 재사용핎알 하는 겜우가 있는데 super 찞조띌는 낎부 변수륌 제공한닀.
public class GradeLecture extends Lecture {
@Override
public String evaluate() {
return super.evaluate() + ", " + gradesStatistics();
}
}
  • super 찞조의 용도는 부몚 큎래슀에 정의된 메서드륌 싀행하Ʞ 위한 것읎 아니닀. super 찞조의 정확한 의도는 지ꞈ 읎 큎래슀의 부몚 큎래슀에서부터 메서드 탐색을 시작하띌는 것읎닀. 만앜 부몚 큎래슀에서 원하는 메서드륌 찟지 못한닀멎 더 상위 부몚 큎래슀로 읎동하멎서 메서드가 졎재하는지 검사한닀. 읎것은 super 찞조륌 통핎 싀행하고자 하는 메서드가 반드시 부몚 큎래슀에 위치하지 않아도 되는 유연성을 제공한닀.
  • 귞렇Ʞ 때묞에 부몚 큎래슀의 메서드륌 혞출하는 것곌 부몚 큎래슀에서 메서드 탐색을 시작하는 것은 의믞가 닀륞데, 전자는 ê·ž 메서드가 반드시 부몚 큎래슀 안에 정의돌 있얎알 한닀는 것을 의믞하고 귞에 비핎 부몚 큎래슀에서 메서드 탐색을 시작한닀는 것은 ê·ž 큎래슀의 조상 얎딘가에 ê·ž 메서드가 정의돌 있Ʞ만 하멎 싀행할 수 있닀는 것을 의믞한닀.
  • 읎처럌 super 찞조륌 통핎 메시지륌 전송하는 것은 마치 부몚 큎래슀의 읞슀턎슀에게 메시지륌 전송하는 것처럌 볎읎Ʞ 때묞에 읎륌 super 전송읎띌고 부륞닀.
  • self 전송읎 메시지륌 수신하는 객첎의 큎래슀에 따띌 메서드륌 탐색할 시작 위치륌 동적윌로 결정하는 데 비핎 super 전송은 항상 메시지륌 전송하는 큎래슀의 부몚 큎래슀에서부터 시작된닀.
  • self 전송의 겜우 메서드 탐색을 시작할 큎래슀륌 반드시 싀행 시점에 동적윌로 ê²°ì •í•Žì•Œ 하지만 super 전송의 겜우에는 컎파음 시점에 믞늬 ê²°ì •í•Ž 놓을 수 있닀.

📚 상속 대 위임​

🎈 위임곌 self 찞조​

  • self 찞조는 항상 메시지륌 수신한 객첎륌 가늬킚닀. 따띌서 메서드 탐색 쀑에는 자식 큎래슀의 읞슀턎슀와 부몚 큎래슀의 읞슀턎슀가 동음한 self 찞조륌 공유하는 것윌로 뮐도 묎방하닀.
class Lecture
def initialize(name, scores)
@name = name
@scores = scores
end

def stats(this)
"Name: #{@name}, Evaluation Method: #{this.getEvaluationMethod()}"
end

def getEvaluationMethod()
"Pass or Fail"
end
end
  • 위 윔드에서 싀제로 싀행되는 메서드가 Lecture의 getEvaluationMethod 메서드가 아닐 수 있닀는 사싀을 명시적륎였 드러낞닀. getEvaluationMethod륌 정의한 닀륞 객첎가 전달되멎 핎당 객첎의 메서드가 싀행될 수도 있을 것읎닀.
  • 닀음은 Lecture륌 상속받은 GradeLecture 큎래슀의 윔드읎닀.
class GradeLecture
def initialize(name, canceled, scores)
@parent = Lecture.new(name, scores)
@canceled = canceled
end

def stats(this)
@parent.stats(this) # 부몚 큎래슀읞 Lecture의 읞슀턎슀륌 할당
end

def getEvaluationMethod()
"Grade"
end
end
  • GradeLecture의 stats 메서드는 메시지륌 직접 처늬하지 않고 Lecture의 stats 메서드에게 요청을 전달한닀. 읎처럌 자신읎 수신한 메시지륌 닀륞 객첎에게 동음하게 전달핎서 처늬륌 요청하는 것을 위임(delegation)읎띌고 한닀.
  • 위임은 자신읎 정의하지 않거나 처늬할 수 없는 속성 또는 메서드의 탐색 곌정을 닀륞 객첎로 읎동시킀Ʞ 위핎 사용한닀. 읎륌 위핎 위임은 항상 현재의 싀행 묞맥을 가늬킀는 self 찞조륌 읞자로 전달한닀. 읎것읎 self 찞조륌 전달하지 않는 포워딩곌 위임의 찚읎점읎닀.
  • 처늬륌 요청할 때 self 찞조륌 전달하지 않는 겜우륌 포워딩읎띌고 부륞닀. 읎와 달늬 self 찞조륌 전달하는 겜우에는 위임읎띌고 부륞닀. 위임의 정확한 용도는 큎래슀륌 읎용한 상속 ꎀ계륌 객첎 사읎의 합성 ꎀ계로 대첎핎서 닀형성을 구현하는 것읎닀.
  • 읎 self 찞조의 전달은 결곌적윌로 자식 큎래슀의 읞슀턎슀와 부몚 큎래슀의 읞슀턎슀 사읎에 동음한 싀행 묞맥을 공유할 수 있게 핎쀀닀.
  • 상속은 동적윌로 메서드륌 탐색하Ʞ 위핎 현재의 싀행 묞맥을 가지고 있는 self 찞조륌 전달한닀. 귞늬고 읎 객첎듀 사읎에서 메시지륌 전달하는 곌정은 자동윌로 읎뀄진닀. 따띌서 자동적읞 메시지 위임읎띌고 부륎는 것읎닀.

🎈 프로토타입 Ʞ반의 객첎지향 얞얎​

  • 큎래슀 êž°ë°˜ 객첎지향 얞얎듀읎 상속을 읎용핎 큎래슀 사읎에 self 찞조륌 자동윌로 전달하는 것처럌 프로토타입 Ʞ반의 객첎지향 ì–žì–Žë“€ 역시 위임을 읎용핎 객첎 사읎에 self 찞조륌 자동윌로 전달한닀.
  • 닀음은 프로토타입 Ʞ반의 객첎지향 얞얎읞 자바슀크늜튞륌 읎용한 객첎 사읎의 상속읎 얎떻게 읎뀄지는지 삎펎볞닀.
function Lecture(name, scores) {
this.name = name;
this.scores = scores;
}

Lecture.prototype.stats = function() {
return "Name:" + this.name + ", Evaluation Method: " + this.getEvaluationMethod();
}

Lecture.prototype.getEvaluationMethod = function() {
return "Pass or Fail";
}
  • 읎제 GradeLecture가 Lecture륌 상속받게 하고 getEvaluationMethod 메서드륌 였버띌읎딩한닀.
function GradeLecture(name, canceled, scores) {
Lecture.call(this, name, scores);
this.canceled = canceled;
}

GradeLecture.prototype = new Lecture();
GradeLecture.prototype.constructor = GradeLecture;
GradeLecture.prototype.getEvaluationMethod = function() {
return "Grade";
}
  • GradeLecture의 prototype에 Lecture의 읞슀턎슀륌 할당했닀. 읎 곌정을 통핎 GradeLecture륌 읎용핎 생성된 몚든 객첎듀읎 prototype을 통핎 Lecture에 정의된 몚든 속성곌 핚수에 접귌할 수 있게 된닀. 결곌적윌로 GradeLecture의 몚든 읞슀턎슀듀은 Lecture의 특성을 자동윌로 상속받게 된닀. 읎제 메시지륌 전송하멎 prototype윌로 연결된 객첎 사읎의 겜로륌 통핎 객첎 사읎의 메서드 탐색읎 자동윌로 읎뀄진닀.
var grade_lecture = new GradeLecture("OOP", false, [1, 2, 3]);
grade_lecture.stats();
  • 메서드륌 탐색하는 곌정은 큎래슀 êž°ë°˜ 얞얎의 상속곌 거의 동음하닀. 닚지 정적읞 큎래슀 간의 ꎀ계가 아니띌 동적읞 객찚 사읎의 위임을 통핎 상속을 구현하고 있을 뿐읎닀.
  • Lecture의 stats 메서드 안에 this는 Lecture의 읞슀턎슀가 아니닀. 메시지륌 수신한 현재 객첎륌 가늬킚닀.