[Java] 추상 클래스에 대해서.araboza

2025. 1. 15. 23:46프로그래밍 언어/Java

개요

객체지향 프로그래밍에 중요한 개념 중 하나인 추상 클래스에 대해서 알아보고자 한다.

 

추상 클래스가 왜 사용이 되는지, 어떻게 사용해야 되는지에 대해서 알아보고,

 

예시까지 더해서 이해를 해보자.

 

 

추상화란?

사전에서 추상화의 뜻

 

사전에서 추상화의 뜻을 검색을 해본다면 사물을 전체를 표현하지 않고 점, 선, 명, 색체만 표현을 해서 그린 그림이라고 나와있다.

 

추상화를 안한 나무

 

추상화 한 나무

 

위의 예시 처럼 전체를 표현한것이 아니라 필요한 것, 본질만 표현을 한 것이라 생각을 하면된다.

프로그래밍 관점에서도 동일하다.

 

프로그래밍에서 추상화는 복잡한 시스템이나 개념을 단순화하고 중요한 부분만 드러내어 다루는 것을 의미한다.

 

 

그렇다면 프로그래밍에서 대표적인 추상화 예시를 확인해보자.

List<String> names = new ArrayList<>();

 

리스트는 내부에 어떤 것이 들어오든, 결론적으로 어떤 Object의 리스트를 사용하게 해준다.

생성 시에는 ArrayList가 사용이 되지만, List의 주요 명령어인 name.add(), name.get() 등 만 알면 사용할 수 있다.

 

이제 그러면 추상클래스를 만들어보자.

 

 

추상 클래스는 어떻게 만들수 있나?

public abstract class Person {
	//맴버 변수
    private String name;
    private int age;
    
    //생성자
    public Person( String name, int age ) {
    	this.name = name;
        this.age = age;
    }
    
    //메소드 (행동)
    public void run() {
		System.out.println("나는 달려간다");
	}
    
    public abstract void job();
}

 

 

클래스 앞에 "abstract"를 추가 하여 추상 클래스를 만들 수 있다

 

하지만 추상클래스가 되는 경우도 있다.

  • 추상 메서드가 1개 이상일 때
  • 추상 클래스를 상속했지만 추상 메서드를 오버라이딩하지 않거나, 인터페이스를 구현했지만, 메서드를 구현하지 않았을 때
  • 추상 메서드가 없는데 강제로 상속하여 사용하려는 경우

이렇게 조심을 하여아한다.

 

그리고 추상 클래스의 사용 조건은 다음을 따른다 

  • 추상클래스는 자기 이름으로 자신의 객체를 생성할 수 없다.
  • 추상클래스는 상송한 자식 클래스의 객체를 생성해서 사용한다.
  • 추상 메서드는 반드시 자식 클래스에서 오버라이딩하여 구현하여야 한다
    그렇지 않으면 자식클래스도 추상 클래스가 된다.
  • 추상 클래스는 반드시 자식을 가져야한다.

 

여기서 나온 개념인 추상 메서드에 대해서 알아보고 다음으로 넘어가자.

 

추상 메서드란?

public abstract class Person {
	//맴버 변수
    private String name;
    private int age;
    
    //생성자
    public Person( String name, int age ) {
    	this.name = name;
        this.age = age;
    }
    
    //메소드 (행동)
    public void run() {
		System.out.println("나는 달려간다");
	}
    
    //추상 메서드
    public abstract void job();
}

 

마지막에 있는 job() 메소드는 우리가 평소에 보던 메소드와 다른 차이가 2가지가 있다.

하나는 앞에 abstract 라는 예약어가 있는 것.

하나는 뒤에 메소드 Body 부분이 없다는 점이다.

 

이것이 추상 메소드의 특징이다.

 

추상 클래스인 Person을 상속받는 자식 클래스는 반드시 job() 메소드를 오버라이딩하여 구현을 하여야한다.

아니면 위의 조건으로 그 자식 클래스 또한 추상 클래스가 된다.

 

 

 

추상 클래스 예시를 확인 해보자.

추상 클래스 이미지

public abstract class Person {

    // 맴버 변수
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void run() {
        System.out.println("나는 달린다.");
    }

    public abstract void job();
}

 

Person이라는 추상 클래스를 만들었다.

간단하게 이름과 나이 그리고 메소드, 추상메소드 한개씩만 만들었다.

 

이제 그러면 상속을 받는 클래스를 만들어 보자.

 

public class Worker extends Person{

    private String job;

    public Worker( String name, int age, String job) {
        super(name, age);
        this.job = job;
    }

    @Override
    public void job() {
        System.out.println("내 일은 " + job);
    }
}
public class Unemployed extends Person {

    private String hobby;

    public Unemployed(String name, int age, String hobby) {
        super(name, age);
        this.hobby = hobby;
    }

    public void setHobby(String hobby) {
        this.hobby = hobby;
    }

    @Override
    public void job() {
        System.out.println("나는 직업이 없어...");
    }
}

 

 

이렇게 Person 클래스를 상속받는 2개의 자식 클래스를 만들었다.

이제 메인에서 어떻게 동작이 되는지 확인을 해보자.

 

public class Main {
    public static void main(String[] args) {

        Person person1 = new Worker("열일남", 20, "프로그래머");

        Person person2 = new Unemployed("게임남", 20, "롤");

        System.out.println("내 이름은" + person1.getName());
        person1.job();

        System.out.println("내 이름은" + person2.getName());
        person2.job();
    }
}

 

메인 클래스를 확인을 해보면 Worker 과 Unemployed 가 Person 클래스에 들어간 모습을 확인을 할 수 있다.

이렇게 되는 이유는 다양성 때문이다.

 

그래서 이제 실행을 해보면

 

 

자식 클래스를 보면 분명 getName() 메소드가 없는데 출력에서 사용이 되는 모습을 볼 수 있다.

 

 

이제 그러면 각 자식 클래스에 메소드를 추가를 해보자.

public class Unemployed extends Person {

    private String hobby;
    private int money;

    public Unemployed(String name, int age, String hobby) {
        super(name, age);
        this.hobby = hobby;
        this.money = 500;
    }

    public void getMoney() {
        System.out.println(this.money);
    }

    /* 생략 */
}

public class Worker extends Person{

    private String job;
    private int money;

    public Worker( String name, int age, String job) {
        super(name, age);
        this.job = job;
        this.money = 5000;
    }

    public void getMoney() {
        System.out.println(this.money);
    }

    /* 생략 */
}

각자 얼마나 돈을 가지고 있는지 확인 하고 싶어서 getMoney() 라는 메소드를 추가를 하였다.

 

그러면 이제 출력이 되는지 확인을 해보자.

 

 

출력하기도 전에 없는 메소드라고 확인을 할 수 있다.

 

왜 이런 것일까?

왜냐하면 우리는 메인 클래스에서 Person 이라고 형을 정해놨기 때문이다.

즉, person1 은 Worker 클래스가 아니라 지금은 Person 클래스이다.

우리가 정한 자식클래스의 메소드를 사용을 하려면 형변환을 해주어야한다.

 

public class Main {
    public static void main(String[] args) {

        Person person1 = new Worker("열일남", 20, "프로그래머");

        Person person2 = new Unemployed("게임남", 20, "롤");

        System.out.println("내 이름은" + person1.getName());
        person1.job();

        Worker worker = (Worker) person1;
        worker.getMoney();

        System.out.println("내 이름은" + person2.getName());
        person2.job();
        Unemployed unemployed = (Unemployed) person2;
        unemployed.getMoney();
    }
}

 

 

다시 부모 클래스에서 자식 클래스로 강제 형 변환을 해주어야 자식 클래스의 메소드를 사용을 할 수 있게 된다.

 

 

느낀점

계속 공부를 하고 배워나가면서 내가 했던 공부들이 수박 겉핥기가 많았다는 것을 알게된다.

요번에 공부를 하면서 모던자바와 클래스에 대해서 자세하게 알아가면서

핵심은 안먹고 겉만 많이 먹었다는 점을 생각을 하게 되었다.

 

내 자신이 발전하는 느낌이 아주 좋다

하루에 하나씩 꾸준히 해봐야겠다.