티스토리 뷰

JAVA

[JAVA] 추상 클래스(Abstract) 완벽 이해하기

까리한 새우 2024. 3. 18. 13:51

추상 클래스란?

 

추상 클래스추상 메서드를 하나라도 가지고 있는 클래스를 만한다.

추상 메서드"메서드가 완성되지 않은, 껍데기만 있는 메서드" 이다.

 

쉽게 이해하기 위해서 예를 들자면 강아지, 고양이, 소 라는 객체가 있다고 하자. 

이 3가지 객체의 공통점은 동물이라는 공톰점이 있겠지요.

그리고 이 객체들은 공통적으로 행위, 즉 "걷기", "먹기",  "울기" 등의 메서드를 가지고 있을 겁니다.

이러한 공통되는 특성을 갖고 있는 것을 추상 클래스라고 합니다.

 

각 동물들은 걷고, 먹는 것은 동일하겠지만 각자 울음소리는 다를 것입니다.

그렇기 때문에 work(), eat() 은 부모 클래스에서 구현해주고 서로 다른 bark() 메서드만 추상 메서드로 선언한것이죠.

그럼 각 자식클래스에서 추상 메서드인 bark() 를 각자에 상황에 맞게 오버라이딩 해주는 것입니다.

 

알아야 할점은 추상 클래스는 추상 메서드를 무조건 하나 이상 가져야 하는 것은 아닙니다.

추상 메서드를 가지지 않고 일반 클래스처럼 독립적인 멤버 변수나, 메서드를 사용할 수 있습니다.

하지만, 추상 메서드를 하나라도 가진다면 해당 클래스는 반드시 추상 클래스가 되어야 한다.

 


 

추상 클래스 특징

 

1. 객체를 직접 생성할 수 없다.

힙 메모리에 생성되는 객체는 내부 요소가 완성되지 않은 상태로 들어갈 수 없기 때문이다.

따라서, 추상 클래스를 상속받는 자식 클래스에서 부모의 추상 메서드를 무조건 오버라이딩 해야한다. 

(강제성 부여)

 

2. 추상 메서드는 선언부만 존재하며 구현부는 존재하지 않는다.

추상 메서드의 구현부를 자식 클래스에서 오버라이딩해서 사용한다.

 

사실, 추상클래스의 문법적인 특징이나 위와같이 객체를 직접 생성할 수 없고, 구현부가 없고 등등... 의 특징 자체는 중요한 부분은 아니다.

추상클래스가 정확히 무엇인지, 왜 사용하는 지의 본질적인 개념을 이해하는 것이 가장 중요한 부분이다.

그래야 자연스럽게 객체 지향 프로그래밍(OOP) 프로그래밍의 추상 클래스 용도를 이해할 수 있다. 

OOP의 특징 중 "추상화" 라는 것은 객체지향의 핵심이며 시작점이 된다.

 


 

추상 클래스 / 추상 메서드 사용 방법

 

추상클래스나 추상메서드를 선언 할 때는 abstract 키워드를 붙여주면 된다.

추상클래스는 오직 상속을 통해 자식 클래스만 만드는 용도로 사용이 된다.

그리고 추상클래스 내에 추상메서드를 생성할 수 있다.

이 메서드는 추상클래스에서 껍데기만 생성해주고 상속받는 자식 클래스에서 필수로 오버라이딩하여 사용합니다.

public abstract class Animal { // 추상 클래스 선언
    
    // 아래와 같이 일반적인 메서드도 사용 가능
    void work() { System.out.println("걷는다."); }
    void eat() { System.out.println("먹는다."); }
    
    abstract void bark(); // 추상 메서드 선언 
}

 

추상 클래스는 아까 설명했던 것처럼 일반 메서드처럼 작성할 수 있습니다.

추상 메서드 뿐 아니라 일반 인스턴스 메서드와 인스턴스 필드들 또한 사용할 수 있습니다.

 

추상 메서드는 자식클래스에서 반드시 오버라이딩을 해야합니다.

자식클래스에서 상속은 받았지만 오버라이딩을 하지 않을 경우 에러가 발생합니다.

public class Dog extends Animal {
    void bark() {
        System.out.println("멍멍");
    }
}
public class Cat extends Animal {
    void bark() {
        System.out.println("야옹");
    }
}

 

위 처럼 자식클래스들에서 부모클래스의 추상메서드를 오버라이딩 해줍니다.

 

부모클래스의 일반적인 인스턴스 메서드는 오버라이딩 하지않아도 호출 시에 부모 메서드가 호출이 됩니다.

추상메서드는 오버라이딩 하지 않으면 오류가 발생하기에 강제로 오버라이딩 해야합니다.

 

< 코드 >

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

        dog.work();  //부모 클래스 Animal 의 work() 실행
        dog.bark();  //추상메서드 구현 후 dog 의 bark() 실행

        cat.eat();   //부모 클래스 Animal 의 eat() 실행
        cat.bark();  //추상메서드 구현 후 cat 의 bark() 실행
    }
}

 

< 결과 >

 


 

추상 클래스 사용 이유

 

추상 클래스를 사용하는 이유에는 크게 2가지가 있다.

 

1. 공통된 필드와 메서드를 통일할 목적

자동차로 예를 들어보자.

여러명의 개발자가 자동차를 상속받아 각자만의 실체 클래스를 구현한다고 생각해보자.

자동차에는 가장 기본적인 기능인 앞으로 나아가는 엑셀과, 멈추는 브레이크가 있을 것이다.

그리고 자동차 모델명이라는 필드도 필요할 것이다.

 

그런데 첫 번째 개발자는 엑셀 기능을 excel(), 브레이크는 break(), 모델 필드명은 carName 이라고 작성했다.

두 번째 개발자는 엑셀 기능을 go(), 브레이크는 noGo(), 모델 필드명은 name() 으로 작성하고,

세 번째 개발자는 엑셀은 straight, 브레이크는 stop(), 모델필드명은 myCar 라고 작성했다.

 

나중에 객체를 바꿔끼워야 할 때 객체 인스턴스만 변경하면 되는 일을 하나하나 각자 다른이름으로 지정했기 때문에, 필드와 메서드를 전부 체크해서 변경해줘야 하는 상황이 생긴다.

즉, 유지보수는 어려워지고 통일성도 개판인 것이다. 그리고 무슨 역할을 하는지 이해가 안되는 경우도 있을 수 있다.

 

그냥 엑셀은 excel(), 브레이크는 break() 이라고 서로 약속한다면 얼마나 쉬운가?

 

따라서, 추상 클래스는 유지보수성은 높이고 통일성을 유지하기 위해 사용한다.

추가로 개발 코드의 가독성도 높아진다.

합쳐서 정리하면, 개발 효율성이 높아진다고 할 수 있겠다.

 

 

2. 실체 클래스 구현 시, 강제성 기능

실체 클래스를 구현 할 때, 개발자가 기능을 구현하다가 까먹는 경우가 있을 것이다.

지금처럼 메서드가 3개밖에 되지 않는다면, 까먹을 일은 없겠지만 만약에 기능을 100개를 구현해야한다고 생각해보자.

 

과연 하나하나 빠짐없이 작성할 수 있을까?

추상 메서드를 사용하지 않는다면, 100개중 1~2개를 까먹고 구현하지 않는다고 해서 에러가 발생하지 않는다.

왜냐면 어차피 부모 클래스의 메서드가 호출되기 때문이다.

하지만 이것은 설계자 즉, 부모 클래스를 구현한 사람이 의도한 결과가 아닐 것이다.

고양이가 울때 "멍멍~" 하고 우는 것이다. 에러는 발생하지 않지 않은가?

 

추상 메서드를 사용할 시, 실체 클래스를 구현 할 때 100개중에 1~2개를 까먹고 기능을 구현하지 않는다면 IDE 에서 오류를 띄워준다.

대충 요런식으로 오류를 발생시킨다.

이렇게 오류를 띄워줌으로써 버그를 방지할 수 있다.

 


 

참고(출처)

https://hyunki99.tistory.com/11

https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EC%B6%94%EC%83%81-%ED%81%B4%EB%9E%98%EC%8A%A4Abstract-%EC%9A%A9%EB%8F%84-%EC%99%84%EB%B2%BD-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0

https://limkydev.tistory.com/188

https://coding-factory.tistory.com/866