[Java] Java 기초 - 추상 클래스 심화

Hyunjun Kim·2025년 4월 1일
0

Data_Engineering

목록 보기
20/153

홍팍 - [자바 기초 34] 추상 클래스

추상 클래스란?
객체를 직접 생성할 수 없는 클래스다. 객체 생성은 불가능하지만, 자식 클래스를 통해 간접적으로 객체를 만들 수 있다.

추상 클래스 형식
abstract 키워드를 사용해 선언한다. 일반 클래스와의 차이는 abstract 키워드가 추가된다는 점이다.

public abstract class Something{
	// 내부 구현 생략..
}

언제 어떻게 사용할까?
어떤 객체가 스스로 존재할 수 없거나 존재하면 안 되는 경우 사용한다.
예를 들어, 다음과 같이 여러 클래스가 동일한 name 필드를 가지고 있다

class Tiger {
	String name; // 중복필드 -> 상속
}

class Rabbit {
	String name;
}

class Lion {
	String name;
}

class Giraffe {
	String name;
}

중복되는 필드를 부모 클래스 Animal로 빼내어 상속하면 코드가 더 간결해진다.

class Animal{
	String name;
}

class Tiger extends Animal{} // -> 클래스를 상속
class Lion extends Animal{}
class Rabbit extends Animal{}
class Giraffe extends Animal{}

Tiger, Lion, Rabbit, Giraffe는 실제로 존재하는 동물이지만, "Animal"이라는 이름의 동물은 존재하지 않는다.

즉, "동물"이라는 개념 자체는 추상적이다.

마찬가지로, 객체지향에서도 "스스로 존재할 수 없는 객체"가 있다.
Animal 객체는 개별적으로 존재해서는 안 되고, Tiger, Lion, Rabbit, Giraffe 같은 구체적인 객체를 위해서만 존재해야 한다.

이럴 때 abstract 키워드를 사용하여 객체 생성을 막을 수 있다.

//Compile Error!
Animal a = new Animal();

추상 클래스는 스스로 객체화할 수 없다는 점을 기억해야 한다.


그렇다면 추상클래스를 왜 사용해야 할까?
상속과 인터페이스의 장점을 모두 가지고 있기 때문이다.

상속의 장점

  • 코드의 중복 제거 (상속을 통한 필드 & 메소드 중복 제거)

인터페이스의 장점

  • 다형석와 그룹화 (한 객체의 다양한 해석, 다양한 객체를 하나의 타입으로 관리)


추상클래스 예제

package com.fastcampus.de.java.study_abstract2.study_abstract;

abstract class Animal{
    String name;

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

    public void eat(){
        System.out.println(name + " is eating.");
    }

    abstract void makeSound();
}


class Dog extends Animal{
    public Dog(String name) {
        super(name);
    }

    @Override
    void makeSound() {
        System.out.println(name + " says : Woof!");
    }
}


class Cat extends Animal{

    public Cat(String name) {
        super(name);
    }

    @Override
    void makeSound() {
        System.out.println(name + " says Meow");
    }
}

public class Main {
    public static void main(String[] args) {
        Dog d1 = new Dog("Buddy");
        Cat c1 = new Cat("Kitty");

        d1.eat();
        d1.makeSound();

        c1.eat();
        c1.makeSound();
    }
}
  • Animal 클래스는 공통된 동작(eat())을 미리 구현하고, 각 동물이 내는 소리(makeSound())는 자식 클래스에서 반드시 구현하도록 강제했다.
  • Dog 와 Cat 은 Animal 을 상속받아 각각 다른 소리를 낸다.


인터페이스 예제

interface Soundable{
    void makeSound();
}

class Dog implements Soundable{

    @Override
    public void makeSound() {
        System.out.println("Woof!");
    }
}

class Cat implements Soundable{

    @Override
    public void makeSound() {
        System.out.println("Meow!");
    }
}

public class Main {
    public static void main(String[] args) {
        Soundable dog = new Dog();
        Soundable cat = new Cat();

        dog.makeSound(); // Woof!
        cat.makeSound(); // Meow!
    }
}
  • Soundable 인터페이스를 만들고, Dog 와 Cat 이 이걸 구현하도록 강제했다.
  • 인터페이스를 사용하면 여러 개의 클래스가 같은 행동을 보장하도록 만들 수 있다.
  • "모든 동물은 소리를 낼 수 있다" 는 개념을 인터페이스로 정의한 것


추상 클래스 + 인터페이스 같이 사용하기

// 🚀 추상 클래스 (공통 기능 제공)
abstract class Vehicle {
    String brand;

    public Vehicle(String brand) {
        this.brand = brand;
    }

    void startEngine() {
        System.out.println(brand + " engine started!");
    }

    abstract void drive();
}

// 🎵 인터페이스 (추가 기능 제공)
interface Playable {
    void playMusic();
}

// 🚗 Car 클래스 (Vehicle 상속 + Playable 구현)
class Car extends Vehicle implements Playable {
    public Car(String brand) {
        super(brand);
    }

    @Override
    void drive() {
        System.out.println(brand + " is driving.");
    }

    @Override
    public void playMusic() {
        System.out.println(brand + " is playing music.");
    }
}

public class Main {
    public static void main(String[] args) {
        Car myCar = new Car("Tesla");

        myCar.startEngine(); // Tesla engine started!
        myCar.drive(); // Tesla is driving.
        myCar.playMusic(); // Tesla is playing music.
    }
}


문제: 직원 관리 시스템
문제 설명:

당신은 직원 관리 시스템을 개발하고 있습니다. 시스템은 각 직원의 기본적인 정보를 관리하며, 모든 직원은 work() 메서드를 통해 각자의 업무를 처리합니다. 그러나 직원마다 업무 처리 방식이 다릅니다.

모든 직원은 Employee라는 추상 클래스를 상속받습니다. 이 클래스는 name과 salary라는 필드와 work()라는 추상 메서드를 가집니다.

Employee 클래스를 상속받은 Manager와 Developer 두 개의 직급 클래스가 있습니다.

Manager 클래스는 work() 메서드를 오버라이드하여 "팀을 관리합니다."라는 메시지를 출력합니다.

Developer 클래스는 work() 메서드를 오버라이드하여 "코드를 작성합니다."라는 메시지를 출력합니다.

각 Employee 객체는 그들의 직급에 맞는 업무를 출력해야 합니다.

Task라는 인터페이스가 있으며, 모든 직원은 Task 인터페이스를 구현하여, taskName이라는 메서드를 통해 현재 수행하는 업무의 이름을 출력합니다.

Manager는 taskName()을 "팀 관리"로, Developer는 "개발"로 출력합니다.

Main 클래스에서 Manager와 Developer 객체를 생성한 뒤, 각각 work()와 taskName() 메서드를 호출하여 결과를 출력하세요.

기본 요구 사항:
1. Employee 추상 클래스와 Manager, Developer 클래스 구현
2. Task 인터페이스 구현
3. work() 메서드를 각 클래스에서 다르게 구현
4. taskName() 메서드를 각 클래스에서 다르게 구현
5. Main 클래스에서 다형성을 이용해 각각의 객체에서 메서드를 호출하고 출력하기

수정 전 코드

// 직원 클래스
class Manager {
    String name;
    double salary;
    
    void work() {
        System.out.println(name + " is managing the team.");
    }
    
    void taskName() {
        System.out.println("Task: Managing the team.");
    }
}

class Developer {
    String name;
    double salary;
    
    void work() {
        System.out.println(name + " is writing code.");
    }
    
    void taskName() {
        System.out.println("Task: Writing code.");
    }
}

public class Main {
    public static void main(String[] args) {
        Manager manager = new Manager();
        manager.name = "Alice";
        manager.salary = 5000;

        Developer developer = new Developer();
        developer.name = "Bob";
        developer.salary = 4000;
        
        // 직무 수행
        manager.work();
        manager.taskName();
        
        developer.work();
        developer.taskName();
    }
}

수정 후 요구 사항:

  • Employee 추상 클래스를 만들고, Manager와 Developer는 이를 상속받습니다.
  • Task 인터페이스를 만들어, Manager와 Developer가 이를 구현하도록 합니다.
  • work() 메서드와 taskName() 메서드를 각 직급에 맞게 구현합니다.
  • Main 클래스에서는 다형성을 활용해 Manager와 Developer 객체를 처리하고, work()와 taskName() 메서드를 호출하여 그들의 업무와 현재 수행 중인 업무의 이름을 출력합니다.

수정 후 코드

package com.fastcampus.de.java.study_abstract;

// Task 인터페이스 정의
interface Task{
    void taskName();
}

// Employee 추상 클래스 정의
abstract class Employee{
    String name;
    double salary;

    // 공통된 업무 메서드는 추상 메서드로 선언
    abstract void work();
}


class Manager extends Employee implements Task{
    @Override
    void work() {
        System.out.println(name + " is managing the team.");
    }

    @Override
    public void taskName() {
        System.out.println("Task: Managing the team.");
    }
}

class Developer extends Employee implements Task{

    @Override
    void work() {
        System.out.println(name + " is writing code.");
    }

    @Override
    public void taskName() {
        System.out.println("Task: Writing code.");
    }
}

public class Main {
    public static void main(String[] args) {
        Employee manager = new Manager();
        manager.name = "Alice";
        manager.salary = 5000;

        Employee developer = new Developer();
        developer.name = "Bob";
        developer.salary = 4000;

        // 직무 수행 및 업무 이름 출력
        manager.work();
        ((Task) manager).taskName(); // Task 인터페이스 메서드 호출

        developer.work();
        ((Task)developer).taskName(); // Task 인터페이스 메서드 호출
    }
}
profile
Data Analytics Engineer 가 되

0개의 댓글