Skip to content

260616_15_장종민#62

Open
MaybeLemon1 wants to merge 6 commits into
SurvivalCodingCampus:student/15_장종민from
MaybeLemon1:master
Open

260616_15_장종민#62
MaybeLemon1 wants to merge 6 commits into
SurvivalCodingCampus:student/15_장종민from
MaybeLemon1:master

Conversation

@MaybeLemon1

Copy link
Copy Markdown

260616_15_장종민

📝 과제 정보

  • 교육 주제: 상속

📋 체크리스트

  • 코드 컨벤션 준수 (Formatter 적용)
  • 모든 테스트 케이스 통과 (JUnit)

📷 실행 결과 (또는 테스트 결과)

image

❓질문 및 어려웠던 점

  • 상속을 언제 사용해야 하는지 아직 조금 헷갈렸다.
  • Override를 할 때 메서드 형식을 부모 클래스와 똑같이 맞춰야 하는 점이 어려웠다.
  • super.attack(hero)로 부모 메서드를 먼저 실행하고, 추가 기능을 작성하는 흐름이 헷갈렸다.
  • 상수로 빼야 하는 값과 매직넘버를 구분하는 게 아직 익숙하지 않았다.
  • PoisonSlime에서 일반 공격 후 남은 HP를 기준으로 독 데미지를 계산하는 부분이 어려웠다.

🔄 자체 평가 & 회고

  • 이번 과제를 통해 상속으로 부모 클래스의 기능을 재사용하는 방법을 이해할 수 있었다.
  • super.attack(hero)를 사용하면서 오버라이드의 흐름을 연습할 수 있었다.
  • 의미 있는 숫자는 상수로 빼면 코드가 더 이해하기 쉬워진다는 점을 알게 되었다.
  • 부모 클래스의 private 필드는 getter와 setter로 접근해야 한다는 점을 다시 확인했다.
  • 상속을 써야 하는 경우는 더 연습이 필요하다고 느꼈다.

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

자바 상속 학습 과제 - PR 검토 요약

📋 과제 개요

이 PR은 자바의 상속(Inheritance) 개념을 실습하는 교육용 과제입니다. Slime, PoisonSlime, Wizard, GreatWizard 클래스들을 구현하여 상속, 메서드 오버라이드, super 호출을 포함한 기본 OOP 개념을 적용했습니다.

✅ 강점

1. 상속 개념의 올바른 이해와 적용

  • PoisonSlime extends SlimeGreatWizard extends Wizard에서 "is-a" 관계가 명확하게 드러남
  • 각 자식 클래스가 부모 클래스의 기능을 물려받고 추가 기능으로 확장하는 패턴이 일관됨

2. 메서드 오버라이드의 정확한 구현

  • PoisonSlimeattack() 메서드에서 super.attack(hero) 호출 후 자식 클래스만의 추가 로직(독 데미지)을 구현
  • GreatWizardheal() 메서드에서 오버라이드할 때 다른 MP 소모량과 회복량을 적용하여 다형성 활용
  • @Override 애너테이션을 사용하여 의도를 명확하게 표현

3. 매직 넘버를 상수로 추출

  • INITIAL_MP, HEAL_POINT, HEAL_MP_COST, POISON_DAMAGE_DIVISOR 등 의미 있는 상수로 분리
  • 코드 가독성을 높이고 유지보수를 용이하게 함

4. 캡슐화 원칙 준수

  • private 필드를 사용하고 public getter/setter로 접근 제어
  • WizardsetMp()에서 음수 값 검증, WizardsetHp()에서 음수를 0으로 보정하는 방어 로직 포함

5. 포괄적인 테스트 커버리지

  • SlimeTest, PoisonSlimeTest, WizardTest, GreatWizardTest 모두 핵심 동작과 경계값 검증
  • 상속 확인(assertInstanceOf, instanceof), 상태 변화 검증, 출력 메시지 검증 등 다층적 테스트
  • MP가 정확히 소모량만큼일 때(healWhenMpIsExactlyTenTest) 같은 경계값 테스트 포함

6. 일관된 설계 패턴

  • 모든 마법 기능에서 "MP 부족 시 메시지 출력 후 조기 반환" 패턴을 일관되게 적용
  • 생성자에서 초기값 설정(GreatWizard의 생성자에서 setMp(INITIAL_MP) 호출)

🤔 성장을 위한 고민점

1. 부모 클래스의 초기화 방식에 대한 질문

GreatWizard 생성자에서 setMp(INITIAL_MP)를 호출하여 부모 클래스(Wizard)의 mp 필드를 초기화하고 있습니다.

  • 현재 방식 외에, 자식 클래스가 부모 클래스의 상태를 초기화하는 다른 방법이 있을까요? (힌트: super() 생성자 활용)

2. 메서드 오버라이드 시 다른 매개변수는 어떻게 처리해야 할까요?

WizardGreatWizardheal() 메서드가 매개변수는 동일하지만 내부 로직이 다릅니다.

  • 만약 두 클래스의 힐 메서드가 다른 개수의 매개변수를 받아야 한다면, 이는 여전히 "오버라이드"라고 할 수 있을까요?

3. 상수 정의 위치에 대한 고려

WizardGreatWizard 모두 INITIAL_MP를 정의하고 있습니다.

  • 공통으로 사용되는 상수는 어디에 정의하는 것이 더 합리적일까요?
  • 자식 클래스에서 부모의 상수에 접근할 수 있을까요?

4. 패키지-프라이빗 메서드의 오버라이드

Slime.attack()package-private(접근 제어자 없음) 메서드인데, PoisonSlime에서 이를 오버라이드할 때 @Override 애너테이션을 붙였습니다.

  • 오버라이드할 때 부모 메서드의 접근 제어자를 더 넓은 범위로 변경하는 것이 좋은 설계일까요? (예: package-privatepublic)

5. super 호출의 타이밍

PoisonSlime.attack()에서 super.attack(hero)를 먼저 호출한 후 독 데미지를 적용합니다.

  • 만약 독 데미지를 먼저 계산하고 일반 공격을 나중에 실행한다면 어떻게 될까요?
  • 메서드의 실행 순서가 중요한 이유는 무엇일까요?

📝 학습 체크리스트

학생의 자기평가에 따르면 다음을 학습했다고 기록했습니다:

  • ✅ 상속을 통한 코드 재사용
  • super를 사용한 부모 메서드 호출 패턴
  • ✅ 매직 넘버를 상수로 추출하여 가독성 향상
  • private 필드 접근 시 getter/setter 필요성
  • ✅ 상속 설계의 "is-a" 관계 이해

다음 학습 제안:

  • 상속이 적절한 시기: 모든 "자식 is-a 부모" 관계가 상속으로 구현되어야 할까요?
  • 다형성 활용: 부모 타입의 참조변수로 여러 자식 객체를 다룰 때의 장점
  • 메서드 오버로딩 vs 오버라이드: 둘의 차이와 사용 시점

🎯 종합 평가

모든 요구사항이 충족되었으며, 코드 품질이 높습니다. 특히 테스트 커버리지의 완성도가 우수합니다. 다음 단계에서는 상속 설계의 우아함과 한계를 더 깊이 이해하는 것을 권장합니다.

Walkthrough

HeroMAX_HP 상수를 추가하고, Slime/PoisonSlimeWizard/GreatWizard 상속 계층을 신규 구현했다. Wizardheal 로직을 MP 기반 고정 회복 방식으로 변경하고, 각 클래스에 대한 JUnit 5 테스트, PlantUML 다이어그램, 연습문제 파일, TIL 문서를 추가했다.

Changes

Slime 상속 계층 구현

Layer / File(s) Summary
Hero 상수 및 Slime/PoisonSlime 구현
game/src/main/java/com/survivalcoding/Hero.java, game/src/main/java/com/survivalcoding/Slime.java, game/src/main/java/com/survivalcoding/PoisonSlime.java
HeroMAX_HP = 100 상수를 추가한다. Slime은 suffix/hp를 보유하고 attack으로 Hero HP를 감소시킨다. PoisonSlimeSlime을 상속하고 attack 오버라이드에서 super.attack 이후 poisonCount > 0일 때 hp / POISON_DAMAGE_DIVISOR 독 피해를 적용하고 카운트를 감소시킨다.
Slime/PoisonSlime JUnit 테스트
game/src/test/java/com/survivalcoding/SlimeTest.java, game/src/test/java/com/survivalcoding/PoisonSlimeTest.java
SlimeTest에서 생성자·초기 HP(50)·attack 시 Hero HP 감소(10)를 검증한다. PoisonSlimeTest에서 상속 관계, poisonCount 초기값(5), 독 피해 소수점 버림, 카운트 소진 후 추가 독 공격 비적용을 검증한다.

Wizard 상속 계층 구현

Layer / File(s) Summary
Wizard heal 로직 변경 및 GreatWizard 구현
game/src/main/java/com/survivalcoding/Wizard.java, game/src/main/java/com/survivalcoding/GreatWizard.java
WizardINITIAL_MP(100)/HEAL_POINT/HEAL_MP_COST 상수를 추가하고 heal을 MP 체크 후 고정 회복/차감 방식으로 변경한다. GreatWizardINITIAL_MP(150)로 초기화하고, heal을 오버라이드(회복량 25, MP 5 소모)하며, superHeal로 Hero HP를 MAX_HP로 완전 회복(MP 50 소모)한다.
Wizard/GreatWizard JUnit 테스트
game/src/test/java/com/survivalcoding/WizardTest.java, game/src/test/java/com/survivalcoding/GreatWizardTest.java
WizardTest의 기존 getter/setter 테스트를 주석 처리하고 초기 MP, heal 성공·출력·MP 부족 시나리오를 검증하는 테스트를 추가한다. GreatWizardTest에서 상속 관계, MP 초기값(150), heal/superHeal HP·MP 변화, System.out 출력 캡처, MP 부족 시 동작 미수행을 검증한다.

설계 문서 및 학습 자료

Layer / File(s) Summary
UML 다이어그램 및 연습문제/TIL 문서
game/puml/260616_연습문제3.puml, game/puml/260616_연습문제4-5.puml, game/src/main/java/com/survivalcoding/260616_연습문제1, game/src/main/java/com/survivalcoding/260616_연습문제2, TIL/2026-06-16-상속.md
Slime/PoisonSlime/Hero 및 Wizard/GreatWizard/Wand/Hero 상속·연관 관계를 PlantUML 클래스 다이어그램으로 정의한다. 잘못된 상속 찾기·부모-자식 클래스 예시 연습문제 텍스트와 상속 핵심 개념·실습 코드·회고를 담은 TIL 문서를 추가한다.

Sequence Diagram(s)

sequenceDiagram
  participant Test as 테스트 코드
  participant PoisonSlime
  participant Slime
  participant Hero

  rect rgba(70, 130, 180, 0.5)
    Note over Test,Hero: PoisonSlime 공격 흐름
    Test->>PoisonSlime: attack(hero)
    PoisonSlime->>Slime: super.attack(hero) — ATTACK_DAMAGE(10) 차감
    Slime->>Hero: setHp(getHp() - 10)
    PoisonSlime->>PoisonSlime: poisonCount > 0 확인
    PoisonSlime->>Hero: setHp(getHp() - getHp()/DIVISOR) — 독 피해
    PoisonSlime->>PoisonSlime: poisonCount -= 1
  end

  rect rgba(60, 179, 113, 0.5)
    Note over Test,Hero: GreatWizard superHeal 흐름
    Test->>PoisonSlime: superHeal(hero)
    PoisonSlime->>PoisonSlime: mp < SUPER_HEAL_MP_COST(50) 확인
    alt MP 부족
      PoisonSlime-->>Test: "마나가 부족합니다" 출력
    else MP 충분
      PoisonSlime->>Hero: setHp(Hero.MAX_HP)
      PoisonSlime->>PoisonSlime: mp -= 50
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive PR 제목이 너무 일반적이고 모호하여 변경 내용을 명확하게 전달하지 못합니다. 제목을 '상속 연습: Slime, PoisonSlime, Wizard, GreatWizard 클래스 구현' 같이 구체적으로 변경하여 PR의 주요 변경 사항을 명확히 나타내세요.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed PR 설명이 제공된 템플릿의 모든 필수 섹션을 포함하고 있으며, 학습 과제 내용, 체크리스트, 실행 결과, 어려웠던 점, 자체 평가를 잘 작성했습니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
game/src/test/java/com/survivalcoding/WizardTest.java (1)

156-163: ⚡ Quick win

System.out 복구는 finally로 보장하는 방식이 안전합니다.

현재는 중간 예외/실패 시 System.out 원복이 누락될 수 있어 테스트 간 간섭 위험이 있습니다. (game/src/test/java/com/survivalcoding/GreatWizardTest.java의 출력 캡처 테스트도 동일 패턴)

수정 예시
 PrintStream originalOut = System.out;
-System.setOut(new PrintStream(outputStream));
-
-wizard.heal(hero);
-
-System.setOut(originalOut);
+try {
+    System.setOut(new PrintStream(outputStream));
+    wizard.heal(hero);
+} finally {
+    System.setOut(originalOut);
+}

Also applies to: 178-185

🤖 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 `@game/src/test/java/com/survivalcoding/WizardTest.java` around lines 156 -
163, The System.out restoration is not guaranteed when exceptions occur because
the System.setOut(originalOut) call is not wrapped in a finally block. Refactor
the code by creating a try-finally block around the System.out redirection logic
in the wizard.heal(hero) test: place the System.setOut(new
PrintStream(outputStream)) before the try block, move wizard.heal(hero) into the
try block, and move System.setOut(originalOut) into the finally block to ensure
restoration regardless of exceptions. Apply this same fix pattern to all other
output capture tests in the file that have the same structure, as well as in
GreatWizardTest.java which has the same issue.
🤖 Prompt for all review comments with 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.

Inline comments:
In `@game/src/main/java/com/survivalcoding/Slime.java`:
- Around line 26-31: The attack method in the Slime class can reduce the Hero's
HP to negative values, which causes PoisonSlime.attack's poison damage
calculation to potentially increase HP instead of decreasing it. In the attack
method, ensure the Hero's HP never goes below 0 by using Math.max() to enforce a
lower bound of 0 when setting the hero's HP after subtracting ATTACK_DAMAGE.
This guarantees the HP lower limit is maintained at the point of basic attack.

In `@game/src/main/java/com/survivalcoding/Wizard.java`:
- Around line 13-23: The heal method in the Wizard class lacks null validation
for the hero parameter, which causes a NullPointerException when heal is called
with a null argument. Add a null check at the beginning of the heal method to
validate that the hero parameter is not null, and return early with an
appropriate error message if it is. The same null validation pattern must also
be applied to the heal and superHeal methods in the GreatWizard class to prevent
the identical NPE vulnerability across all these public methods.

In `@TIL/2026-06-16-상속.md`:
- Around line 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.
- Around line 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.

---

Nitpick comments:
In `@game/src/test/java/com/survivalcoding/WizardTest.java`:
- Around line 156-163: The System.out restoration is not guaranteed when
exceptions occur because the System.setOut(originalOut) call is not wrapped in a
finally block. Refactor the code by creating a try-finally block around the
System.out redirection logic in the wizard.heal(hero) test: place the
System.setOut(new PrintStream(outputStream)) before the try block, move
wizard.heal(hero) into the try block, and move System.setOut(originalOut) into
the finally block to ensure restoration regardless of exceptions. Apply this
same fix pattern to all other output capture tests in the file that have the
same structure, as well as in GreatWizardTest.java which has the same issue.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f6d1cc3a-fa7f-42a8-a747-d769bd96fe63

📥 Commits

Reviewing files that changed from the base of the PR and between 0f89b5b and cc97938.

📒 Files selected for processing (14)
  • TIL/2026-06-16-상속.md
  • game/puml/260616_연습문제3.puml
  • game/puml/260616_연습문제4-5.puml
  • game/src/main/java/com/survivalcoding/260616_연습문제1
  • game/src/main/java/com/survivalcoding/260616_연습문제2
  • game/src/main/java/com/survivalcoding/GreatWizard.java
  • game/src/main/java/com/survivalcoding/Hero.java
  • game/src/main/java/com/survivalcoding/PoisonSlime.java
  • game/src/main/java/com/survivalcoding/Slime.java
  • game/src/main/java/com/survivalcoding/Wizard.java
  • game/src/test/java/com/survivalcoding/GreatWizardTest.java
  • game/src/test/java/com/survivalcoding/PoisonSlimeTest.java
  • game/src/test/java/com/survivalcoding/SlimeTest.java
  • game/src/test/java/com/survivalcoding/WizardTest.java

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

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

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 | 🟠 Major | ⚡ Quick win

HP 하한 미보장으로 독 추가타가 역으로 HP를 올릴 수 있습니다.

기본 공격에서 HP를 음수로 내릴 수 있어, 이후 PoisonSlime.attack의 독 데미지 계산이 음수가 되는 경로가 생깁니다. 기본 공격 시점에서 HP 하한(0)을 보장해 주세요.

수정 제안
     void attack(Hero hero) {
         System.out.println("슬라임 " + suffix + "이/가 공격했다");
         System.out.println(ATTACK_DAMAGE + "의 데미지");
 
-        hero.setHp(hero.getHp() - ATTACK_DAMAGE);
+        int nextHp = Math.max(0, hero.getHp() - ATTACK_DAMAGE);
+        hero.setHp(nextHp);
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
void attack(Hero hero) {
System.out.println("슬라임 " + suffix + "이/가 공격했다");
System.out.println(ATTACK_DAMAGE + "의 데미지");
hero.setHp(hero.getHp() - ATTACK_DAMAGE);
}
void attack(Hero hero) {
System.out.println("슬라임 " + suffix + "이/가 공격했다");
System.out.println(ATTACK_DAMAGE + "의 데미지");
int nextHp = Math.max(0, hero.getHp() - ATTACK_DAMAGE);
hero.setHp(nextHp);
}
🤖 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 `@game/src/main/java/com/survivalcoding/Slime.java` around lines 26 - 31, The
attack method in the Slime class can reduce the Hero's HP to negative values,
which causes PoisonSlime.attack's poison damage calculation to potentially
increase HP instead of decreasing it. In the attack method, ensure the Hero's HP
never goes below 0 by using Math.max() to enforce a lower bound of 0 when
setting the hero's HP after subtracting ATTACK_DAMAGE. This guarantees the HP
lower limit is maintained at the point of basic attack.

Comment on lines 13 to 23
public void heal(Hero hero) {
int basePoint = 10;
int recovPoint = (int) (basePoint * this.wand.getPower());
hero.setHp(hero.getHp() + recovPoint);
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());
}

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 | 🟠 Major | ⚡ Quick win

heal 공개 API에 null 입력 방어가 없어 NPE가 발생할 수 있습니다.

Line 19/22에서 hero를 바로 역참조하므로 heal(null) 호출 시 즉시 실패합니다. 같은 패턴이 game/src/main/java/com/survivalcoding/GreatWizard.javaheal/superHeal에도 반복됩니다. 메서드 초입에서 명시적으로 검증해 크래시를 예방하는 편이 안전합니다.

수정 예시
 public void heal(Hero hero) {
+    if (hero == null) {
+        throw new IllegalArgumentException("대상 hero는 null일 수 없습니다.");
+    }
     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());
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public void heal(Hero hero) {
int basePoint = 10;
int recovPoint = (int) (basePoint * this.wand.getPower());
hero.setHp(hero.getHp() + recovPoint);
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 void heal(Hero hero) {
if (hero == null) {
throw new IllegalArgumentException("대상 hero는 null일 수 없습니다.");
}
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());
}
🤖 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 `@game/src/main/java/com/survivalcoding/Wizard.java` around lines 13 - 23, The
heal method in the Wizard class lacks null validation for the hero parameter,
which causes a NullPointerException when heal is called with a null argument.
Add a null check at the beginning of the heal method to validate that the hero
parameter is not null, and return early with an appropriate error message if it
is. The same null validation pattern must also be applied to the heal and
superHeal methods in the GreatWizard class to prevent the identical NPE
vulnerability across all these public methods.

Comment thread TIL/2026-06-16-상속.md
Comment on lines +115 to +245
```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());
}
}

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.

Comment thread TIL/2026-06-16-상속.md
Comment on lines +240 to +244
hero.setHp(Hero.MAX_HP);
setMp(getMp() - SUPER_HEAL_MP_COST);

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

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant