🦔

포켓몬 육성과 같이 오브젝트 지향을 가족으로 배울 수 있는 Java 초클래스 입문 ~"좋아"가 배움으로 바뀌는 마법의 도감~

に公開

📚 목차


🧩 제1장 포켓몬 도감 만들기! 클래스 설계도

🎯 1.1 오브젝트란?

🌟 포켓몬도 오브젝트? 실생활에서 느끼는 객체지향의 세계

포켓몬 세계에서 피카츄나 파이리 같은 모든 포켓몬은 '오브젝트'입니다.
현실 세계로 예를 들면, '자전거'나 '스마트폰'도 오브젝트입니다.

오브젝트에는 두 가지 요소가 있습니다:

  • 🧬 속성 (필드): 예를 들어 피카츄의 '타입', '레벨' 등
  • 🛠 행동 (메서드): 피카츄가 사용하는 '10만 볼트'나 '울음소리' 같은 기술

🏗 1.2 클래스를 만들자

📘 도감에 싣기 전에! 클래스라는 설계도의 중요성

클래스란 '오브젝트의 설계도'입니다.
예를 들어 '포켓몬'이라는 클래스를 만들면, 이를 바탕으로 피카츄, 파이리 등 다양한 포켓몬 오브젝트를 만들 수 있습니다.


🛠 클래스 정의 예시 (Java)

public class Pokemon {
    String name; // 포켓몬 이름
    String type; // 타입 (예: 전기)
    int level;   // 레벨

    public void introduce() {
        System.out.println("안녕! 나는 " + name + ", 타입은 " + type + ", 레벨은 " + level + "이야!");
    }
}

🧪 1.3 정리와 테스트

✅ 클래스의 핵심 정리

요소 설명
클래스 설계도 (포켓몬의 공통 정보)
오브젝트 실제로 생성된 피카츄나 파이리 등
속성 이름, 타입, 레벨
메서드 자기소개 등의 행동

📝 연습문제

  1. 자신만의 포켓몬을 생각해서 Pokemon 클래스로 표현해 보세요.
  2. introduce() 메서드에 '사용 가능한 기술'도 소개할 수 있도록 확장해 보세요.

🎯 제2장 몬스터볼에서 GET! 인스턴스 생성과 사용법

🧱 2.1 인스턴스를 만들자

🥚 포켓몬을 소환하라! new 연산자와 생성자의 마법

Java에서는 new 키워드를 사용하여 클래스에서 오브젝트(인스턴스)를 생성합니다.
이것은 마치 몬스터볼에서 포켓몬을 꺼내는 것과 같습니다.

Pokemon pika = new Pokemon();
  • Pokemon: 설계도(클래스)의 이름
  • pika: 생성된 피카츄 인스턴스
  • new Pokemon(): 새로운 포켓몬을 만드는 마법의 주문

🔓 2.2 게터와 세터의 사용법

🔍 "너의 HP는 몇이야?" 게터로 상태 확인

public String getName() {
    return name;
}

💪 "레벨 업!" 세터로 포켓몬을 강화하자

public void setLevel(int lvl) {
    level = lvl;
}

게터와 세터를 사용하는 이유는?
-> 캡슐화(encapsulation) 를 활용하기 위해서입니다.


🧬 캡슐화란?

클래스 내부 데이터를 외부에서 직접 변경하지 못하게 막고, 안전하게 다루는 방법

❌ 잘못된 예 (캡슐화 안됨)

public class Pokemon {
    public int hp;
}
...
pikachu.hp = -100; // 위험한 직접 접근!

✅ 올바른 예 (게터와 세터 사용)

public class Pokemon {
    private int hp;

    public void setHp(int hp) {
        if (hp >= 0) {
            this.hp = hp;
        } else {
            System.out.println("HP는 0 이상이어야 합니다.");
        }
    }

    public int getHp() {
        return this.hp;
    }
}

🛠 2.3 메서드를 추가하자

⚡ "10만 볼트!" 기술을 사용하는 메서드

public void useMove() {
    System.out.println(name + "의 " + move + "! 효과는 굉장했다!");
}

✅ 고급편: @Override 와 toString

@Override
public String toString() {
    return "포켓몬: " + name + " / 타입: " + type + " / 레벨: " + level + " / 기술: " + move;
}

@Override는 필수는 아니지만, 매우 권장됩니다.


🎯 2.4 정리와 테스트

용어 설명
인스턴스 new 연산자로 만든 객체
게터 값을 읽는 메서드
세터 값을 설정하는 메서드
@Override 부모 클래스 메서드를 재정의할 때 사용
toString 객체 정보를 문자열로 출력

💻 최종 예시 코드

public class Pokemon {
    private String name;
    private String type;
    private int level;
    private String move;

    public void setName(String name) { this.name = name; }
    public void setType(String type) { this.type = type; }
    public void setLevel(int level) { this.level = level; }
    public void setMove(String move) { this.move = move; }

    public String getName() { return name; }
    public String getType() { return type; }
    public int getLevel() { return level; }
    public String getMove() { return move; }

    public void useMove() {
        System.out.println(name + "의 " + move + "! 효과는 굉장했다!");
    }

    @Override
    public String toString() {
        return "포켓몬: " + name + " / 타입: " + type + " / 레벨: " + level + " / 기술: " + move;
    }
}

public class Main {
    public static void main(String[] args) {
        Pokemon pika = new Pokemon();
        pika.setName("피카츄");
        pika.setType("전기");
        pika.setLevel(25);
        pika.setMove("10만 볼트");

        System.out.println(pika);
        pika.useMove();
    }
}

🧩 이 예시로 클래스 → 인스턴스 생성 → 값 설정 → 메서드 실행의 흐름을 체험할 수 있습니다!


🛡 제3장 포켓몬의 비밀을 지켜라! 클래스의 구조

🔐 3.1 다른 클래스에서의 접근 제한

🧳 "그 HP는 보여줄 수 없어!" private와 public의 경계선

public class Pokemon {
    private int hp;

    public int getHp() {
        return hp;
    }

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

📝 private은 비공개, public은 공개를 의미합니다.


🧩 회색 지대 속성! 아무 것도 안 썼을 때의 세계

접근 제한자를 생략하면 동일한 패키지 내에서는 접근 가능합니다. 이를 default 접근이라고 부릅니다.


🛡 비밀스러운 스테이터스를 보호하라! 캡슐화의 진수

**캡슐화(encapsulation)**는 데이터를 클래스 안에 감추고 외부에서 직접 접근을 막는 개념입니다.
포켓몬 게임에서 상태창을 트레이너만 조작할 수 있는 것처럼요!


⚙️ 3.2 멤버의 구조

⚡ "모든 포켓몬의 공통 특성" static 멤버의 힘

public class Pokemon {
    public static final int MAX_LEVEL = 100;
}

📝 static은 클래스 전체에서 공유되는 변수 또는 메서드를 의미합니다.


🧬 "이 피카츄만의 특성" 인스턴스 멤버의 개성

public class Pokemon {
    private int level; // 인스턴스마다 개별 보유
}

⚠️ static과 instance, 공존 가능할까?

가능합니다. 차이를 명확히 이해하는 것이 중요합니다.

public class Pokemon {
    public static final int MAX_LEVEL = 100;
    private int level;

    public boolean isMaxLevel() {
        return level == MAX_LEVEL;
    }
}

🧱 3.3 생성자의 구조

🌀 같은 이름, 다른 방식? 오버로딩의 마법

public Pokemon() {
    this.name = "피카츄";
}

public Pokemon(String name) {
    this.name = name;
}

🌈 "컬러풀 포켓몬" 다양한 생성자 만들기

public Pokemon(String name, int level, String type) {
    this.name = name;
    this.level = level;
    this.type = type;
}

🔁 this()로 생성자 연결하기

public Pokemon(String name, int level) {
    this(name, level, "노말");
}

🧊 아무 것도 지정하지 않아도 생기는 포켓몬?

생성자를 정의하지 않으면 Java가 자동으로 기본 생성자를 생성해줍니다.
하지만 다른 생성자가 정의되어 있으면 기본 생성자는 자동 생성되지 않으므로 주의!


🧪 3.4 정리와 테스트

📘 3장 요약: 캡슐화와 멤버 관리 핵심

항목 내용
접근 제한자 데이터 노출 여부 설정
private 외부에서 볼 수 없음
public 어디서나 사용 가능
static 모든 포켓몬 공통 특성
생성자 포켓몬 생성 시 초기 설정 메서드
this 자기 자신을 가리킴, 생성자 호출 가능

🎮 테스트 코드

public class Pokemon {
    private String name;
    private String type;
    private int level;
    public static final int MAX_LEVEL = 100;

    public Pokemon() {
        this("피카츄", 5, "전기");
    }

    public Pokemon(String name, int level, String type) {
        this.name = name;
        this.level = level;
        this.type = type;
    }

    public String getName() { return name; }
    public String getType() { return type; }
    public int getLevel() { return level; }

    public void setLevel(int level) { this.level = level; }

    public boolean isMaxLevel() {
        return level == MAX_LEVEL;
    }

    public void introduce() {
        System.out.println("나는 " + name + "! 타입: " + type + ", 레벨: " + level);
    }
}

public class Main {
    public static void main(String[] args) {
        Pokemon pika = new Pokemon();
        pika.introduce();

        Pokemon eve = new Pokemon("이브이", 10, "노말");
        eve.introduce();

        System.out.println("이브이는 최대 레벨인가? " + eve.isMaxLevel());
    }
}

🧩 이 예시로 캡슐화, static 변수, 생성자 오버로딩을 체험할 수 있습니다!


🧭 제4장 포켓몬을 가리켜라! 인스턴스와 참조의 비밀

🎯 4.1 참조란?

🎈 몬스터볼과 포켓몬의 관계? 참조의 신비한 역할

Java에서 참조(reference)란 오브젝트의 **위치(주소)**를 저장하는 변수입니다.
몬스터볼은 포켓몬을 담는 그릇이죠. Java에서는 이 몬스터볼이 참조입니다.

Pokemon pika1 = new Pokemon(); // 포켓몬 생성
Pokemon pika2 = pika1;         // pika1과 같은 포켓몬을 가리킴

📝 pika2pika1같은 포켓몬 인스턴스를 참조할 뿐, 새로 생성된 것이 아닙니다.


👀 4.2 참조를 의식하자

🧪 "복사했는데 같은 포켓몬?" 변수 간 대입의 진실

pika2.setLevel(50);
System.out.println(pika1.getLevel()); // 50이 출력됨

📌 오브젝트는 복사되는 것이 아니라 참조 주소만 전달되는 것이 기본입니다.


🧊 4.3 완전히 불변의 클래스를 만들자

🧼 "전설의 포켓몬, 아무도 바꿀 수 없다!" 완전 불변 구현

public final class Pokemon {
    private final String name;
    private final String type;
    private final int level;

    public Pokemon(String name, String type, int level) {
        this.name = name;
        this.type = type;
        this.level = level;
    }

    public String getName() { return name; }
    public String getType() { return type; }
    public int getLevel() { return level; }
}

🛡 모든 필드에 final을 붙이면 값이 한 번 정해지면 변경 불가한 클래스가 됩니다.


🧬 4.4 참조형

🧪 "기본형과의 차이는?" Java의 타입 시스템

타입 종류 예시 특징
기본형 (primitive) int, double 값 자체를 저장
참조형 (reference) String, Pokemon 객체의 주소를 저장

⚠️ "비어있는 몬스터볼?" null의 정체

Pokemon pika = null;
pika.introduce(); // NullPointerException!

🚨 null은 아직 객체가 생성되지 않은 상태를 의미하며, 사용 시 에러 발생!


🧪 4.5 정리와 테스트

🧾 4장 요약: 참조와 불변 이해

키워드 설명
참조 객체의 위치를 가리킴
같은 참조 변수 대입 시 같은 객체를 가리킴
null 아무 것도 없는 상태
불변 (immutable) 값이 절대 변경되지 않는 설계

💻 종합 테스트 코드

public final class Pokemon {
    private final String name;
    private final String type;
    private final int level;

    public Pokemon(String name, String type, int level) {
        this.name = name;
        this.type = type;
        this.level = level;
    }

    public String getName() { return name; }
    public String getType() { return type; }
    public int getLevel() { return level; }

    public void introduce() {
        System.out.println("나는 " + name + "! 타입: " + type + ", 레벨: " + level);
    }
}

public class Main {
    public static void main(String[] args) {
        Pokemon pika1 = new Pokemon("피카츄", "전기", 25);
        Pokemon pika2 = pika1; // 동일 참조

        pika1.introduce();
        pika2.introduce();

        // 아래 코드는 NullPointerException을 발생시킴
        // Pokemon empty = null;
        // empty.introduce();
    }
}

🔍 이 예시로 참조 개념과 null 위험성을 직접 체험할 수 있습니다!


🧪 제5장 오리지널 포켓몬을 설계하라! 오브젝트 모델링

🧱 5.1 기능을 구현하는 클래스

📝 어떤 포켓몬을 만들까? 전체적인 구조를 생각해보자

포켓몬 게임에서는 이름, 타입, 레벨, 기술 등 다양한 정보가 필요합니다.
이 정보를 클래스로 어떻게 표현할지를 생각하는 것이 바로 오브젝트 모델링입니다.


🧮 "특성과 기술을 정하자" 클래스 정의 예시

public class Pokemon {
    private String name;
    private String type;
    private int level;
    private String[] moves;

    public Pokemon(String name, String type, int level, String[] moves) {
        this.name = name;
        this.type = type;
        this.level = level;
        this.moves = moves;
    }

    public void introduce() {
        System.out.println("이름: " + name + " / 타입: " + type + " / 레벨: " + level);
        System.out.println("기술 목록:");
        for (String move : moves) {
            System.out.println("- " + move);
        }
    }
}

🚀 "정말 이대로 괜찮을까?" 설계 검토 포인트

  • 기술은 몇 개까지 허용할까? → 배열로 유연하게 대응
  • 레벨 상한은? → static final 사용
  • 여러 타입 지원? → 이번엔 단일 타입만 사용

⚙️ 구현 시작! 클래스 → 코드 변환

String[] fushiMoves = {"몸통 박치기", "덩굴채찍", "독가루"};
Pokemon fushi = new Pokemon("이상해씨", "풀", 10, fushiMoves);
fushi.introduce();

📦 5.2 record

🆕 Java 16 신기능! record는 누구인가?

Java 16부터는 record로 간단한 데이터 클래스를 정의할 수 있습니다.

public record SimplePokemon(String name, String type, int level) {}

✨ "간편 도감 등록" record의 장점

  • 생성자
  • getter
  • toString()
  • equals()

이 모든 기능이 자동으로 생성됩니다.

SimplePokemon zenigame = new SimplePokemon("꼬부기", "물", 8);
System.out.println(zenigame.name());
System.out.println(zenigame);

🧠 기능 확장도 가능

public record SimplePokemon(String name, String type, int level) {
    public boolean isStarter() {
        return level <= 10;
    }
}

🔒 전설 포켓몬은 변하지 않는다! 불변 record

record의 필드는 모두 final이므로 수정 불가능.
뮤츠나 루기아 같은 전설의 포켓몬에 적합한 구조입니다.


🪄 3줄로 포켓몬 만들기!

SimplePokemon hitokage = new SimplePokemon("파이리", "불꽃", 5);
System.out.println("팀 후보: " + hitokage.name());

🎯 5.3 정리와 테스트

✅ 모델링과 record 요약

개념 설명
모델링 현실의 개체를 프로그램 구조로 설계
필드 이름, 타입, 기술 등 정보
메서드 소개, 기술 사용 등 동작
record 간단한 불변 데이터 클래스
불변 값이 절대 바뀌지 않는 구조

💻 종합 예제 코드

public class Pokemon {
    private String name;
    private String type;
    private int level;
    private String[] moves;

    public Pokemon(String name, String type, int level, String[] moves) {
        this.name = name;
        this.type = type;
        this.level = level;
        this.moves = moves;
    }

    public void introduce() {
        System.out.println("이름: " + name + " / 타입: " + type + " / 레벨: " + level);
        System.out.println("기술:");
        for (String move : moves) {
            System.out.println("- " + move);
        }
    }
}

public record SimplePokemon(String name, String type, int level) {
    public boolean isStarter() {
        return level <= 10;
    }
}

public class Main {
    public static void main(String[] args) {
        String[] moves = {"몸통 박치기", "덩굴채찍", "독가루"};
        Pokemon bulbasaur = new Pokemon("이상해씨", "풀", 10, moves);
        bulbasaur.introduce();

        SimplePokemon charmander = new SimplePokemon("파이리", "불꽃", 5);
        System.out.println("팀 후보: " + charmander.name());
        System.out.println("스타터? " + charmander.isStarter());
    }
}

🧩 이 예시로 클래스와 record를 활용한 데이터 정의를 모두 경험할 수 있습니다!


🧬 제6장 진화의 비밀을 밝혀라! 상속이라는 혈통

📊 6.1 클래스 다이어그램

📚 "포켓몬 도감의 뒷면" 클래스 다이어그램 해독법

Java의 상속은 도감의 진화 트리와 유사합니다.
예) 피츄 → 피카츄 → 라이츄

Pokemon (부모 클래스)
  └── Pikachu (자식 클래스)
       └── Raichu (손자 클래스)

📝 공통 정보는 부모 클래스에, 진화하면서 생기는 속성은 자식 클래스에 작성합니다.


🧪 6.2 상속

👪 "피츄에서 피카츄로!" 클래스 확장하기

public class Pokemon {
    String name;
    String type;

    public void introduce() {
        System.out.println("나는 " + name + ", 타입은 " + type + "!");
    }
}

public class Pikachu extends Pokemon {
    int level;

    public void thunderbolt() {
        System.out.println(name + "의 10만 볼트!");
    }
}

📝 Pikachu는 Pokemon을 상속받아 levelthunderbolt를 추가합니다.


🐣 "부화 과정" 생성자 호출 순서

public class Pokemon {
    public Pokemon() {
        System.out.println("포켓몬이 태어났다!");
    }
}

public class Pikachu extends Pokemon {
    public Pikachu() {
        System.out.println("피카츄가 태어났다!");
    }
}

출력 순서:

포켓몬이 태어났다!
피카츄가 태어났다!

🚀 상속 효과 실감하기

Pikachu pika = new Pikachu();
pika.name = "피카츄";
pika.type = "전기";
pika.level = 15;

pika.introduce();     // 부모 메서드
pika.thunderbolt();   // 자식 메서드

📜 6.3 상속 규칙

🧠 "○○은 ○○이다" Is-a 관계

"피카츄는 포켓몬이다" → 상속 가능
"포켓몬은 피카츄이다" → ❌ 역상속 불가


🚫 "전설은 복제 불가!" 상속 금지 클래스

public final class Mewtwo extends Pokemon {
    // 상속 불가 클래스
}

📝 final 클래스는 더 이상 상속할 수 없습니다.


🔒 "봉인된 특성" Sealed 클래스 (Java 17+)

public sealed class Pokemon permits Pikachu, Eevee {}

Pokemon은 Pikachu와 Eevee만 상속 가능


🧙 "전수된 기술" 상속 불가 멤버

  • private 필드/메서드 → 자식 클래스에서 접근 불가
  • final 메서드 → 오버라이드 불가

🎯 6.4 정리와 테스트

📘 상속 요약

개념 설명
상속 (extends) 클래스 기능을 이어받음
부모 클래스 공통 기능 보유
자식 클래스 개별 기능 추가
Is-a 관계 ○○는 ××이다
final 상속 금지
sealed 상속 가능한 클래스 제한 (Java 17+)

💻 종합 예제 코드

public class Pokemon {
    String name;
    String type;

    public void introduce() {
        System.out.println("나는 " + name + ", 타입은 " + type + "!");
    }
}

public class Pikachu extends Pokemon {
    int level;

    public Pikachu(String name, int level) {
        this.name = name;
        this.type = "전기";
        this.level = level;
    }

    public void thunderbolt() {
        System.out.println(name + "의 10만 볼트! 효과는 굉장했다!");
    }
}

public class Main {
    public static void main(String[] args) {
        Pikachu pika = new Pikachu("피카츄", 20);
        pika.introduce();
        pika.thunderbolt();
    }
}

🧩 이 예제로 상속, 생성자 호출 순서, 메서드 오버라이드의 흐름을 익힐 수 있습니다!


🌳 제7장 포켓몬 가계의 미스터리! 상속 관계를 마스터하라

🧭 7.1 상속 트리

🧬 "포켓몬 진화 가계도" 상속 트리 전체 구조

포켓몬은 진화 계통을 가지며, Java에서도 상속 트리로 표현할 수 있습니다.

예) 나옹 → 나이킹 (지역 진화)

Pokemon
├── Meowth
│   └── Perrserker
└── Eevee
    ├── Vaporeon
    ├── Jolteon
    └── Flareon

🧬 모든 포켓몬의 조상, Object 클래스

Java의 모든 클래스는 암묵적으로 Object 클래스를 상속합니다.

public class Pikachu {
    // Object 클래스의 toString(), equals() 사용 가능
}

🔗 7.2 생성자의 연결

👶 부모 → 자식으로 이어지는 생성자 호출

public class Pokemon {
    public Pokemon(String name) {
        System.out.println(name + " 이(가) 태어났습니다!");
    }
}

public class Eevee extends Pokemon {
    public Eevee() {
        super("이브이");
        System.out.println("이브이 준비 완료!");
    }
}

출력 결과:

이브이 이(가) 태어났습니다!
이브이 준비 완료!

❓ super() 생략 시 행동

부모 클래스에 인자가 없는 기본 생성자가 있으면 super()는 생략 가능
단, 부모 생성자에 매개변수가 있다면 반드시 명시 필요!


📁 7.3 여러 클래스를 하나의 파일에 정의하기

public class Pokemon {
    String name;
    public void greet() {
        System.out.println("안녕! 나는 " + name + "!");
    }
}

class Pikachu extends Pokemon {
    public Pikachu() {
        name = "피카츄";
    }
}

📝 파일명이 Pokemon.java일 경우, public 클래스는 하나만 존재해야 하며 나머지는 public 없이 작성


🛡 7.4 protected 접근자

🏠 "가족에게만 보이는 특성" protected의 특별한 힘

public class Pokemon {
    protected String name; // 같은 패키지와 자식 클래스에서 접근 가능
}

⚠️ 주의사항

  • 외부 패키지에서는 접근 불가
  • public, private와 구분하여 정확히 사용

🌐 접근 제어자 정리

접근자 동일 클래스 동일 패키지 자식 클래스 외부 클래스
public
protected ×
(default) × ×
private × × ×

🎯 7.5 정리와 테스트

🧾 7장 요약: Java의 상속 가계도 완전 이해!

개념 설명
상속 트리 클래스 간 부모-자식 구조
Object 클래스 모든 클래스의 조상
super() 부모 생성자 호출
protected 같은 패키지, 자식 클래스 접근 가능

💻 종합 예제 코드

public class Pokemon {
    protected String name;

    public Pokemon(String name) {
        this.name = name;
        System.out.println(name + " 이(가) 태어났습니다!");
    }

    public void greet() {
        System.out.println("안녕하세요! 저는 " + name + " 입니다!");
    }
}

class Pikachu extends Pokemon {
    public Pikachu() {
        super("피카츄");
        System.out.println("피카츄 준비 완료!");
    }

    public void thunderbolt() {
        System.out.println(name + " 의 10만 볼트!");
    }
}

public class Main {
    public static void main(String[] args) {
        Pikachu pika = new Pikachu();
        pika.greet();
        pika.thunderbolt();
    }
}

🎉 이 장에서는 상속 구조, protected, 클래스 파일 내 동시 정의 등을 모두 마스터할 수 있습니다!


🌀 제8장 포켓몬의 변신술! 참조의 자동 형변환

🪄 8.1 참조의 자동 형변환

🔁 "메타몽의 변신" 자식 → 부모 클래스 자동 형변환

Java에서는 자식 클래스의 인스턴스를 부모 클래스 타입으로 자동 형변환할 수 있습니다.

Pokemon pika = new Pikachu("피카츄", 20);
pika.introduce(); // 부모 메서드는 사용 가능

📝 이처럼 Pikachu 타입 인스턴스를 Pokemon 타입으로 저장 → 업캐스팅


🧱 8.2 업캐스트와 다운캐스트

⬆️ 업캐스트 (자동)

Pokemon poke = new Pikachu("피카츄", 20);

⬇️ 다운캐스트 (명시적)

Pikachu pika = (Pikachu) poke;
pika.thunderbolt(); // 자식 클래스의 메서드 사용 가능

⚠️ poke가 Pikachu가 아닐 경우, ClassCastException 발생


🔍 8.3 instanceof 연산자

🧪 "진짜 피카츄인가?" instanceof로 타입 확인

if (poke instanceof Pikachu) {
    Pikachu pika = (Pikachu) poke;
    pika.thunderbolt();
}

📝 안전한 다운캐스트를 위한 타입 체크 필수


🧠 8.4 switch를 이용한 타입 판별 (Java 14~)

switch (poke) {
    case Pikachu p -> p.thunderbolt();
    case Eevee e -> e.adapt();
    default -> System.out.println("알 수 없는 포켓몬");
}

📝 패턴 매칭 switch를 활용하면 코드가 간결해집니다


🎯 8.5 정리와 테스트

📘 변신의 기술 요약

용어 설명
업캐스트 자식 → 부모 (자동)
다운캐스트 부모 → 자식 (명시적)
instanceof 타입 안전 확인
switch 패턴 Java 14+ 타입 분기 가능

💻 종합 예제 코드

public class Pokemon {
    protected String name;

    public Pokemon(String name) {
        this.name = name;
    }

    public void introduce() {
        System.out.println("나는 " + name + " 입니다!");
    }
}

class Pikachu extends Pokemon {
    private int level;

    public Pikachu(String name, int level) {
        super(name);
        this.level = level;
    }

    public void thunderbolt() {
        System.out.println(name + " 의 10만 볼트! 레벨: " + level);
    }
}

class Eevee extends Pokemon {
    public Eevee(String name) {
        super(name);
    }

    public void adapt() {
        System.out.println(name + " 는 다양한 타입으로 진화할 수 있다!");
    }
}

public class Main {
    public static void main(String[] args) {
        Pokemon poke1 = new Pikachu("피카츄", 25); // 업캐스트
        Pokemon poke2 = new Eevee("이브이");

        if (poke1 instanceof Pikachu) {
            Pikachu pika = (Pikachu) poke1;
            pika.thunderbolt();
        }

        switch (poke2) {
            case Pikachu p -> p.thunderbolt();
            case Eevee e -> e.adapt();
            default -> System.out.println("알 수 없는 포켓몬");
        }
    }
}

🧩 이 예제로 업캐스트, 다운캐스트, instanceof, switch 패턴을 마스터할 수 있습니다!


🎭 제9장 하나의 기술로 다양한 효과! 다형성(Polymorphism)

💡 9.1 오버로드

🧠 "같은 이름, 다른 효과" 오버로드의 원리

Java에서는 같은 이름의 메서드를 매개변수의 수나 타입이 다르면 여러 개 정의할 수 있습니다.

public void attack(String move) {
    System.out.println("「" + move + "」을(를) 사용했다!");
}

public void attack(String move, int times) {
    System.out.println("「" + move + "」을(를) " + times + "번 연속 사용했다!");
}

🔁 9.2 오버라이드

🔄 "진화로 기술이 강화되었다!" 오버라이드의 개념

오버라이드는 부모 클래스의 메서드를 자식 클래스에서 재정의하는 것입니다.

public class Pokemon {
    public void attack() {
        System.out.println("기본 공격!");
    }
}

public class Pikachu extends Pokemon {
    @Override
    public void attack() {
        System.out.println("피카츄의 10만 볼트!");
    }
}

📝 @Override는 재정의임을 명확히 하여 실수를 방지합니다.


🌀 9.3 다형성 (Polymorphism)

🎨 "같은 기술, 다른 효과" 다형성의 마법

다형성이란 부모 클래스 타입으로 자식 클래스 메서드를 실행할 수 있는 기능입니다.

Pokemon poke = new Pikachu();
poke.attack(); // 실제로는 Pikachu의 attack() 실행됨

📝 호출 시점의 타입이 아닌 실제 인스턴스 타입에 따라 동작 → 동적 바인딩


✨ 트레이너가 기술을 사용할 때

public class Trainer {
    public void usePokemon(Pokemon p) {
        p.attack();
    }
}

포켓몬의 종류와 관계없이 attack() 호출 가능


🎯 9.4 정리와 테스트

✅ 다형성 요약 표

개념 설명
오버로드 같은 이름, 다른 인자
오버라이드 부모 메서드 재정의
다형성 부모 타입으로 자식 메서드 실행
동적 바인딩 실행 시점에 맞는 메서드 호출

💻 종합 예제 코드

public class Pokemon {
    public void attack() {
        System.out.println("기본 공격!");
    }

    public void attack(String move) {
        System.out.println("「" + move + "」을(를) 사용했다!");
    }

    public void attack(String move, int times) {
        System.out.println("「" + move + "」을(를) " + times + "번 사용했다!");
    }
}

class Pikachu extends Pokemon {
    @Override
    public void attack() {
        System.out.println("피카츄의 10만 볼트!");
    }
}

class Eevee extends Pokemon {
    @Override
    public void attack() {
        System.out.println("이브이의 스피드스타!");
    }
}

class Trainer {
    public void usePokemon(Pokemon p) {
        p.attack();
    }
}

public class Main {
    public static void main(String[] args) {
        Trainer ash = new Trainer();

        Pokemon pika = new Pikachu();
        Pokemon eevee = new Eevee();

        ash.usePokemon(pika);   // 피카츄의 기술
        ash.usePokemon(eevee);  // 이브이의 기술

        pika.attack("아이언 테일");      // 오버로드 예시
        pika.attack("전광석화", 2);      // 오버로드 예시
    }
}

🧩 이 코드로 오버로드, 오버라이드, 다형성 전부를 실습할 수 있습니다!


🦄 제10장 환상의 포켓몬의 비밀! 추상 클래스의 세계

🧩 10.1 추상 클래스란?

👻 "도감에 없는 미지의 포켓몬" 추상 클래스의 정의

추상 클래스 (abstract class)는 불완전한 설계도입니다.
직접 인스턴스화할 수 없으며, 자식 클래스에서 구현을 완성해야 합니다.

public abstract class Pokemon {
    String name;
    String type;

    public void introduce() {
        System.out.println("나는 " + name + "! 타입은 " + type + "!");
    }

    public abstract void specialMove(); // 추상 메서드
}

📝 추상 메서드는 몸체가 없으며, 반드시 자식 클래스에서 정의 필요


🧬 10.2 추상 클래스 상속

🌟 "전설에서 현실로" 추상 메서드 구현

public class Mew extends Pokemon {
    public Mew() {
        name = "뮤";
        type = "에스퍼";
    }

    @Override
    public void specialMove() {
        System.out.println("뮤의 사이코키네시스!");
    }
}

📝 자식 클래스에서 반드시 specialMove()를 오버라이드 해야 함


✨ 중간 추상화 계층 만들기

public abstract class LegendaryPokemon extends Pokemon {
    public abstract void summon(); // 추가 추상 메서드
}

🗺 10.3 추상 클래스 다이어그램

<<abstract>> Pokemon
    + introduce()
    + specialMove() ← abstract

       ▲
       |
     Mew (specialMove() 구현)

📝 <<abstract>>는 추상 클래스를 나타냅니다


🎯 10.4 정리와 테스트

✅ 요약 표

항목 설명
abstract class 불완전한 설계도, 직접 생성 불가
abstract method 자식 클래스에서 구현 필요
강제 구현 추상 메서드가 있으면 반드시 구현
중간 추상화 추상 클래스끼리도 상속 가능

💻 종합 예제 코드

public abstract class Pokemon {
    protected String name;
    protected String type;

    public void introduce() {
        System.out.println("나는 " + name + "! 타입은 " + type + "!");
    }

    public abstract void specialMove();
}

class Mew extends Pokemon {
    public Mew() {
        this.name = "뮤";
        this.type = "에스퍼";
    }

    @Override
    public void specialMove() {
        System.out.println(name + "의 사이코키네시스!");
    }
}

public class Main {
    public static void main(String[] args) {
        Pokemon mystery = new Mew(); // 추상 클래스 타입으로 인스턴스 사용
        mystery.introduce();
        mystery.specialMove();
    }
}

🧩 이 예제로 추상 클래스, 오버라이드, 다형성까지 경험할 수 있습니다!


🔗 제11장 포켓몬의 타입과 능력! 인터페이스의 힘

💡 11.1 인터페이스란?

🔍 "비행 타입", "물 타입" → 인터페이스 정의

Java의 interface기능의 계약서입니다.
공통된 능력을 여러 클래스가 구현할 수 있도록 합니다.

public interface Flyable {
    void fly(); // 비행 타입이면 반드시 구현
}

🧪 "복수 타입을 가진 포켓몬" → 클래스에 인터페이스 구현

public class Charizard implements Flyable {
    @Override
    public void fly() {
        System.out.println("리자몽이 하늘을 날았다!");
    }
}

📝 implements 키워드로 "이 클래스는 날 수 있다"고 명시


✨ 인터페이스 메서드는 모두 public abstract

즉, 수정 불가능하고 구현 강제됨
공통된 기능을 동일하게 사용하도록 설계된 구조


🔁 11.2 인터페이스 타입으로 형변환

🔄 "타입으로써의 포켓몬" 인터페이스 타입 사용

Flyable flyingPokemon = new Charizard();
flyingPokemon.fly(); // 리자몽의 fly() 호출됨

🎯 "물 타입으로 사용" WaterType 예시

public interface WaterType {
    void surf();
}

public class Vaporeon implements WaterType {
    @Override
    public void surf() {
        System.out.println("샤미드가 파도를 탄다!");
    }
}

🎭 11.3 인터페이스 기반 다형성

🧬 "같은 기술 이름, 다양한 클래스" 다형성 실현

public void useFly(Flyable f) {
    f.fly();
}

📝 클래스와 무관하게 Flyable 인터페이스만 구현되어 있으면 실행 가능


🧪 "실전: 타입별 기술 효과"

Flyable f1 = new Charizard();
Flyable f2 = new Pidgeot();

f1.fly(); // 리자몽
f2.fly(); // 피죤투

🌐 11.4 인터페이스 상속

🔗 "복합 타입 포켓몬" 다중 상속 구현

public interface FlyingWaterType extends Flyable, WaterType {
    void dive();
}

🧠 인터페이스도 상속된다!

public class Gyarados implements FlyingWaterType {
    @Override
    public void fly() {
        System.out.println("갸라도스가 날아올랐다!");
    }

    @Override
    public void surf() {
        System.out.println("갸라도스가 파도를 탔다!");
    }

    @Override
    public void dive() {
        System.out.println("갸라도스가 잠수했다!");
    }
}

🎯 11.5 정리와 테스트

✅ 요약 표

항목 설명
interface 기능 계약, 타입과 유사
implements 인터페이스 구현
다중 구현 여러 타입 동시 보유 가능
다형성 클래스 구분 없이 기능 호출 가능

💻 종합 예제 코드

public interface Flyable {
    void fly();
}

public interface WaterType {
    void surf();
}

public class Charizard implements Flyable {
    @Override
    public void fly() {
        System.out.println("리자몽이 하늘을 날았다!");
    }
}

public class Vaporeon implements WaterType {
    @Override
    public void surf() {
        System.out.println("샤미드가 파도를 탔다!");
    }
}

public class Gyarados implements Flyable, WaterType {
    @Override
    public void fly() {
        System.out.println("갸라도스가 날아올랐다!");
    }

    @Override
    public void surf() {
        System.out.println("갸라도스가 파도를 쳤다!");
    }
}

public class Main {
    public static void main(String[] args) {
        Flyable f = new Charizard();
        WaterType w = new Vaporeon();
        Flyable fw = new Gyarados();

        f.fly();
        w.surf();
        fw.fly();

        if (fw instanceof WaterType water) {
            water.surf(); // 갸라도스도 surf 가능
        }
    }
}

🧩 이 장에서는 인터페이스, 다형성, 다중 구현을 완전히 이해할 수 있습니다!

Discussion