Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
270 changes: 270 additions & 0 deletions TIL/2026-06-16-상속.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
# 2026-06-16 상속

## 오늘 배운 내용

- 상속
- 기존 클래스를 바탕으로 새로운 클래스를 만드는 기능
- "extends" 키워드를 사용해서 부모 클래스를 상속받음
- 부모 클래스에 있는 필드와 메서드는 자식 클래스에서 사용할 수 있음
- 자식 클래스에는 부모 클래스에 없는 기능을 추가로 작성할 수 있음
- 상속 관계는 보통 "자식 클래스 is-a 부모 클래스" 관계일 때 사용
- 예제
- public class Hero { // 부모 클래스
String name;
int hp;

public void attack() {
}
}

public class SuperHero extends Hero { 자식 클래스
boolean flying;

public void fly() {
flying = true;
}
}
SuperHero sh = new SuperHero();

sh.name = "슈퍼맨"; // 부모 클래스의 필드 사용
sh.hp = 100; // 〃 의 필드 사용
sh.attack(); // 〃 의 메서드 사용
sh.fly(); // 자식 클래스에서 추가한 메서드 사용

- Override
- Override는 부모 클래스에 있는 메서드를 자식 클래스에서 다시 정의하는 것
- 같은 이름, 같은 매개변수, 같은 반환 타입으로 작성해야 함
- 자식 클래스에서 재정의한 메서드가 우선적으로 실행됨
- @Override를 붙이면 오타나 형식 오류를 컴파일러가 확인해줘서 좋음
- 예제
- public class Hero {
public void run() {
System.out.println("도망쳤다.");
}
}
public class SuperHero extends Hero {
@Override
public void run() {
System.out.println("하늘을 날아서 도망쳤다.");
}
}
- 출력: 하늘을 날아서 도망쳤다.

- super와 생성자 호출 순서
- super는 부모 클래스의 멤버에 접근할 때 사용
- super.메서드명()을 사용하면 부모 클래스의 메서드를 호출할 수 있음
- super()는 부모 클래스의 생성자를 호출할 때 사용
- 자식 클래스의 객체가 생성될 때는 부모 생성자가 먼저 실행되고, 그 다음 자식 생성자가 실행됨
- 자식 생성자 안에 super()를 직접 작성하지 않으면, 컴파일러가 자동으로 super()를 추가
- super() 또는 super(값)은 생성자 안에서 첫 줄에 작성해야 함
- 예제
- public class Hero {
public Hero() {
System.out.println("부모 생성자 실행");
}
}

public class SuperHero extends Hero {
public SuperHero() {
super();
System.out.println("자식 생성자 실행");
}
}

- SuperHero sh = new SuperHero(); 를 실행하면 생성자가 호출된다.
- 호출 순서는 부모 생성자 -> 자식 생성자 순서이다.
- 출력: 부모 생성자 실행
자식 생성자 실행

- 부모 생성자에 매개변수가 있을 경우
- 부모 클래스에 기본 생성자가 없고, 매개변수가 있는 생성자만 있다면
자식 클래스에서 반드시 super(값)을 작성해야 한다.
- 예제
- public class Hero {
String name;

public Hero(String name) {
this.name = name;
}
}
public class SuperHero extends Hero {
public SuperHero(String name) {
super(name);
}

- SuperHero sh = new SuperHero("슈퍼맨"); 를 실행하면
"슈퍼맨" 값이 super(name)을 통해 부모 생성자로 전달된다.
- 즉, SuperHero 생성자가 실행되기 전에 부모 클래스인 Hero의 생성자가 먼저 실행된다.

- 정리
- 생성자 호출 순서는 항상 부모 생성자 -> 자식 생성자 이다.
- 부모 생성자에 필요한 값이 있다면 자식 생성자에서 super(name)처럼 값을 넘겨줘야 한다.

## 기억할 것

- 상속은 기존 클래스의 기능을 재사용하기 위해 사용
- extends를 사용해서 부모 클래스를 상속받음
- 부모 클래스의 필드와 메서드는 자식 클래스에서 사용할 수 있음
- 부모 메서드를 자식 클래스에서 다시 작성하는 것을 오버라이드라고 함
- super는 부모 클래스의 메서드나 생성자를 호출할 때 사용함
- 생성자는 상속되지 않지만, 자식 객체가 생성될 때 부모 생성자가 먼저 실행됨
- final이 붙은 클래스는 상속할 수 없고, final이 붙은 메서드는 오버라이드할 수 없음

## 실습 코드

```java
package com.survivalcoding;

public class Slime {
private static final int DEFAULT_HP = 50;
private static final int ATTACK_DAMAGE = 10;

final String suffix;
int hp = DEFAULT_HP;

public Slime(String suffix) {
this.suffix = suffix;
}

public String getSuffix() {
return suffix;
}

public int getHp() {
return hp;
}

public void setHp(int hp) {
this.hp = hp;
}

void attack(Hero hero) {
System.out.println("슬라임 " + suffix + "이/가 공격했다");
System.out.println(ATTACK_DAMAGE + "의 데미지");

hero.setHp(hero.getHp() - ATTACK_DAMAGE);
}
}

public class PoisonSlime extends Slime {
private static final int INITIAL_POISON_COUNT = 5;
private static final int POISON_DAMAGE_DIVISOR = 5;

private int poisonCount = INITIAL_POISON_COUNT;

public PoisonSlime(String suffix) {
super(suffix);
}

@Override
void attack(Hero hero) {
super.attack(hero);
if (poisonCount > 0) {
System.out.println("추가로, 독 포자를 살포했다!");

int poisonDamage = hero.getHp() / POISON_DAMAGE_DIVISOR;
hero.setHp(hero.getHp() - poisonDamage);

System.out.println(poisonDamage + "포인트 데미지");

poisonCount--;
}
}

public int getPoisonCount() {
return poisonCount;
}
}
public class Wizard {
private static final int INITIAL_MP = 100;
private static final int HEAL_POINT = 20;
private static final int HEAL_MP_COST = 10;

private int hp;
private int mp = INITIAL_MP;
private String name;
private Wand wand;

public void heal(Hero hero) {
if (mp < HEAL_MP_COST) {
System.out.println("마나가 부족합니다");
return;
}

hero.setHp(hero.getHp() + HEAL_POINT);
mp -= HEAL_MP_COST;

System.out.println("힐을 시전했습니다. 대상 HP: " + hero.getHp());
}

public int getMp() {
return mp;
}
public void setMp(int mp) {
if (mp < 0) {
throw new IllegalArgumentException("마법사의 MP는 0 이상이어야 한다.");
}
this.mp = mp;
}
}

public class GreatWizard extends Wizard {
private static final int INITIAL_MP = 150;
private static final int HEAL_POINT = 25;
private static final int HEAL_MP_COST = 5;
private static final int SUPER_HEAL_MP_COST = 50;

public GreatWizard() {
setMp(INITIAL_MP);
}

@Override
public void heal(Hero hero) {
if (getMp() < HEAL_MP_COST) {
System.out.println("마나가 부족합니다");
return;
}

hero.setHp(hero.getHp() + HEAL_POINT);
setMp(getMp() - HEAL_MP_COST);

System.out.println("힐을 시전했습니다. 대상 HP: " + hero.getHp());
}

public void superHeal(Hero hero) {
if (getMp() < SUPER_HEAL_MP_COST) {
System.out.println("마나가 부족합니다");
return;
}

hero.setHp(Hero.MAX_HP);
setMp(getMp() - SUPER_HEAL_MP_COST);

System.out.println("슈퍼 힐을 시전했습니다. 대상 HP: " + hero.getHp());
}
Comment on lines +240 to +244

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Hero.MAX_HP 기준을 문서와 구현에서 통일해 주세요.

Line 240에서 Hero.MAX_HP를 참조하지만, 같은 PR의 다른 산출물은 GreatWizard 쪽 최대 체력 상수를 기준으로 설명하고 있습니다. 상수 소유 위치를 하나로 맞춰야 superHeal 계약이 헷갈리지 않습니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@TIL/2026-06-16-상속.md` around lines 240 - 244, The superHeal method
implementation uses Hero.MAX_HP as the reference constant, but this
inconsistency with how the method contract is documented elsewhere creates
confusion. Review the documentation and other related code in the PR to
determine the correct ownership of the max HP constant being used in the
superHeal method, then update either the constant reference in the superHeal
method implementation or the documentation to ensure both use the same constant
(either Hero.MAX_HP or GreatWizard's max HP constant) consistently throughout.

}
Comment on lines +115 to +245

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Check if the file exists and view the specified lines
fd -type f "2026-06-16-상속.md" 2>/dev/null || echo "File not found with fd"

# Also try a direct path check
if [ -f "TIL/2026-06-16-상속.md" ]; then
  echo "File found at TIL/2026-06-16-상속.md"
  wc -l TIL/2026-06-16-상속.md
  echo "---"
  sed -n '115,245p' TIL/2026-06-16-상속.md
else
  echo "File not found at expected path"
fi

Repository: SurvivalCodingCampus/polytech_java2

Length of output: 3036


🌐 Web query:

Java multiple public classes in single file compilation rule

💡 Result:

In Java, a single source file (.java file) can contain multiple top-level class or interface declarations, but it is subject to specific rules regarding the public modifier [1][2][3]. The primary rule is that a compilation unit (a single.java file) can contain at most one public top-level class or interface [1][2]. If a file contains a public class, that class must have the same name as the file (e.g., a public class named MyClass must be defined in a file named MyClass.java) [3][4]. Additional details regarding this rule include: 1. Package-Private Classes: Other classes or interfaces defined in the same file that are not marked as public (i.e., package-private classes) do not have this naming restriction [1][3]. They are accessible only to other code within the same package [3]. 2. Rationale: While the Java Language Specification (JLS) allows host systems to enforce this restriction to simplify the process of locating class files within a file system, it is effectively mandatory in the standard Oracle Java compiler [5][6][7]. 3. Compilation: When multiple classes are defined in one file, compiling that file will generate separate.class files for each class defined [6]. 4. Alternatives: If you need to group multiple classes together, consider using nested classes (inner or static nested classes) instead of multiple top-level classes in a single file, as this is often considered a cleaner design [1][2]. Note: When running Java source code directly (without explicit compilation, available in newer versions of Java), the restriction may behave differently depending on the execution environment, but it remains a standard practice for maintainable, professional Java development [1].

Citations:


실습 코드 블록을 파일 단위로 분리해 주세요.

이 예제는 Slime, PoisonSlime, Wizard, GreatWizard 네 개의 public class를 한 코드 블록에 작성했는데, Java 컴파일 규칙상 하나의 파일에는 최대 하나의 public class만 포함될 수 있습니다. 현재 코드를 그대로 붙여넣으면 컴파일 오류가 발생합니다. 각 클래스를 별도 파일로 분리한 예제로 변경하거나, 발췌용 의사 코드임을 명시하기 바랍니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@TIL/2026-06-16-상속.md` around lines 115 - 245, The code block contains four
public classes (Slime, PoisonSlime, Wizard, and GreatWizard) in a single file,
which violates Java's compilation rule that allows only one public class per
file. Either separate each public class into its own individual file
(Slime.java, PoisonSlime.java, Wizard.java, GreatWizard.java) or modify the code
block to make all but one of the classes package-private (remove the public
modifier) and add a note clarifying this is pseudocode for reference purposes
only.

```

## 어려웠던 점

- 상수로 빼야 하는 값과 그냥 숫자로 써도 되는 값을 구분하는 게 아직 익숙하지 않았다.
- Override를 할 때 메서드 이름, 매개변수, 반환 타입이 부모 클래스랑 똑같아야 한다는 점이 헷갈렸다.
- super.attack(hero)처럼 부모 클래스의 메서드를 먼저 실행하고, 그 뒤에 추가 기능을 작성하는 흐름이 처음에는 어려웠다.
- PoisonSlime에서 일반 공격을 먼저 하고, 남은 HP를 기준으로 독 데미지를 계산하는 부분이 조금 어려웠다.

## 해결 방법

- 반복해서 사용하는 숫자나 의미가 있는 숫자는 상수로 빼서 이름을 붙이려고 했다.
- Override는 @Override를 붙여서 오타나 형식이 틀렸을 때 바로 확인할 수 있게 했다.
- super.attack(hero)를 사용하면 부모 클래스의 공격을 먼저 실행하고,
그 다음에 독 공격 같은 추가 기능을 넣을 수 있다는 식으로 이해했다.
- 테스트 코드를 작성하면서 일반 공격 데미지와 독 데미지가 순서대로 잘 적용되는지 확인했다.

## 내일 더 공부할 것

- 상속을 써야 하는 경우와 그냥 클래스를 따로 만드는 경우의 차이를 더 공부할 것이다.
- Override와 Overload의 차이가 아직 헷갈려서 예제로 다시 정리해볼 것이다.
- super()와 super.메서드명()의 차이를 더 확실하게 공부할 것이다.
- 상수와 매직넘버를 구분해서 코드에 적용하는 방법을 더 연습할 것이다.
- 부모 생성자와 자식 생성자의 호출 순서를 코드로 더 연습해볼 것이다.
- JUnit으로 상속과 오버라이드가 제대로 동작하는지 테스트하는 방법을 더 연습할 것이다.
34 changes: 34 additions & 0 deletions game/puml/260616_연습문제3.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
@startuml

class Slime {
~ final suffix : String
~ hp : int
~ slimeDamage : int

+ Slime(suffix : String)
+ getSuffix() : String
+ getHp() : int
+ setHp(hp : int) : void
~ attack(hero : Hero) : void
}

class PoisonSlime {
- poisonCount : int

+ PoisonSlime(suffix : String)
~ attack(hero : Hero) : void
+ getPoisonCount() : int
}

class Hero {
- hp : int

+ getHp() : int
+ setHp(hp : int) : void
}

PoisonSlime --|> Slime
Slime ..> Hero : attack(hero)
PoisonSlime ..> Hero : attack(hero)

@enduml
61 changes: 61 additions & 0 deletions game/puml/260616_연습문제4-5.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
@startuml

class Hero {
- name : String
- hp : int

~ attack() : void
+ getHp() : int
+ setHp(hp : int) : void
}

class Wand {
- name : String
- power : double

+ getName() : String
+ setName(name : String) : void
+ getPower() : double
+ setPower(power : double) : void
}

class Wizard {
- {static} INITIAL_MP : int
- {static} HEAL_POINT : int
- {static} HEAL_MP_COST : int

- hp : int
- mp : int
- name : String
- wand : Wand

+ heal(hero : Hero) : void
+ getHp() : int
+ setHp(hp : int) : void
+ getMp() : int
+ setMp(mp : int) : void
+ getName() : String
+ setName(name : String) : void
+ getWand() : Wand
+ setWand(wand : Wand) : void
}

class GreatWizard {
- {static} INITIAL_MP : int
- {static} HEAL_POINT : int
- {static} HEAL_MP_COST : int
- {static} SUPER_HEAL_MP_COST : int
- {static} HERO_MAX_HP : int

+ GreatWizard()
+ heal(hero : Hero) : void
+ superHeal(hero : Hero) : void
}

GreatWizard --|> Wizard

Wizard --> Wand : wand
Wizard ..> Hero : heal(hero)
GreatWizard ..> Hero : heal(hero), superHeal(hero)

@enduml
11 changes: 11 additions & 0 deletions game/src/main/java/com/survivalcoding/260616_연습문제1
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
다음 중에서 "잘못 된 상속"인 것을 모두 구하시오
슈퍼클래스 서브클래스
1 Person Student
2 Car Engine
3 Father Child
4 Food Susi
5 SuperMan Man

예) Man extends SuperMan: X

답) 2, 3, 5
9 changes: 9 additions & 0 deletions game/src/main/java/com/survivalcoding/260616_연습문제2
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
다음 클래스에 대해 "부모 클래스"와 "자식 클래스"를 1개씩 생각 해 보시오.

예) Character <- Hero <- SuperHero

1. [Device] <- Phone <- [SmartPhone]

2. [Vehicle] <- Car <- [SportsCar]

3. [Book] <- Dictionary <- [Korean Dictionary]
Loading