관리 메뉴

나만을 위한 블로그

[JAVA] 추상 클래스 vs 인터페이스 본문

JAVA

[JAVA] 추상 클래스 vs 인터페이스

참깨빵위에참깨빵 2021. 3. 14. 01:24
728x90
반응형

인터페이스에 관해선 예전에 포스팅을 작성한 적이 있다.

onlyfor-me-blog.tistory.com/48

 

[JAVA] 인터페이스란?

20.10.06 - 코드블럭에 코드 삽입 인터페이스 사전적 정의 : 접속기, 접점(두 가지 주제시스템 등이 서로 만나서 영향을 주고받는 영역) 지식백과 : 사물과 사물 사이 또는 사물과 인간 사이의 경계

onlyfor-me-blog.tistory.com

그런데 인터페이스와 비슷한 것이 있는데 이것이 추상 클래스다.

그래서 이번 포스팅에선 추상 클래스가 무엇인지 간단하게 확인한 다음, 추상 클래스와 인터페이스의 공통점, 차이점을 정리하려고 한다. 둘이 뭔 차이가 있고 왜 쓰냐고 묻는다면 대답하기 어려웠기 때문인 것도 있다.

 

 

먼저 추상 클래스다. 추상이라는 단어는 아래와 같은 뜻이 있다.

여러 가지 사물이나 개념에서 공통되는 특성이나 속성 따위를 추출하여 파악하는 작용

그리고 추상적이라는 말은 아래와 같은 뜻이 있다.

어떤 사물이 직접 경험하거나 지각할 수 있는 일정한 형태와 성질을 갖추고 있지 않은 것,
구체성이 없이 사실이나 현실에서 멀어져 막연하고 일반적인. 또는 그런 것

이야기를 하다 보면 그 설명은 추상적이라 이해가 잘 안 된다, 말하는 게 추상적이라 뭘 말하려는 건지 모르겠다 등등 추상적이라는 단어를 종종 듣곤 한다. 나는 말하는 내용이 너무 막연해서 정확히 무엇을 말하고자 하는지 모를 때 추상적이라는 말을 쓴다고 생각한다.

 

그럼 자바에서 추상 클래스는 뭘 뜻하는 말일까? 검색한 결과 대강 공통적인 내용은 아래와 같다.

  • 추상 클래스는 1개 이상의 추상(abstract) 메서드를 포함하는 클래스다. 추상 메서드는 선언부만 있고 본체는 없는 메서드를 말한다
  • 추상 클래스는 객체화할 수 없다. 어떤 클래스가 추상 클래스를 상속해서 추상 클래스에 선언된 추상 메서드를 구현하는 경우, 이 클래스는 객체화할 수 있다.
  • 추상 메서드가 1개라도 있다면 그 클래스는 abstract를 붙여서 추상 클래스로 선언해야 한다.

이외의 내용을 종합해보면, 추상 클래스는 그 자체로 클래스로서의 역할을 수행하진 못하지만 공통된 기능을 가진 채 다른 클래스에게 상속되어 부모 클래스가 될 경우, 자식 클래스를 완성시키는 역할을 한다.

 

그런데 추상 클래스에 대한 내용을 찾아보다가 추상화라는 키워드를 알게 됐다. 추상화의 내용은 아래와 같다.

www.javatpoint.com/abstract-class-in-java

 

Abstract Class in Java - Javatpoint

Abstract class in java with abstract methods and examples. An abstract class can have abstract and non-abstract (concrete) methods and can't be instantiated with inheritance, polymorphism, abstraction, encapsulation, exception handling, multithreading, IO

www.javatpoint.com

추상화는 구현 세부 정보를 숨기고 사용자에게 기능만 표시하는 프로세스다. 또 다른 방법은 사용자에게 필수 사항만 표시하고 내부의 세부 정보를 숨긴다...(중략)...추상화를 사용하면 객체가 수행하는 방식 대신 객체가 수행하는 작업에 집중할 수 있다.
자바에서 추상화를 구현하는 방법은 추상 클래스, 인터페이스가 있다.

ko.wikipedia.org/wiki/%EC%B6%94%EC%83%81%ED%99%94_(%EC%BB%B4%ED%93%A8%ED%84%B0_%EA%B3%BC%ED%95%99)

 

추상화 (컴퓨터 과학)

위키백과, 우리 모두의 백과사전.

ko.wikipedia.org

복잡한 자료, 모듈, 시스템 등으로부터 핵심적인 개념 또는 기능을 간추려 내는 것

en.wikipedia.org/wiki/Abstraction_(computer_science)

 

Abstraction (computer science)

From Wikipedia, the free encyclopedia Jump to navigation Jump to search Technique for arranging complexity of computer systems The essence of abstraction is preserving information that is relevant in a given context, and forgetting information that is irre

en.wikipedia.org

더 중요한 세부 사항에 주의를 집중하기 위해 물체 또는 시스템 연구에서 물리적, 공간적 또는 시간적 세부 사항 또는 속성을 제거하는 프로세스. 일반화 프로세스와 본질적으로 유사하다. 또는 다양한 비 추상적 대상 또는 연구 시스템의 공통적인 특징, 속성을 미러링해서 추상적인 개념 및 대상을 만드는 것이다.

정리해보면 필수적이면서 공통적으로 존재하는 속성, 기능을 골라내는 것이 추상화라고 할 수 있을 듯하다.

그럼 추상 클래스에는 추상 클래스를 상속하게 될 클래스들의 공통적인 속성, 기능을 정리해서 추상 or 비 추상 메서드로 만들어서 정의해 둔 다음, 공통 기능 및 속성은 추상 클래스를 상속해서 구현하는 것이 추상 클래스가 존재하는 이유이지 않을까 생각한다.

 

 

이제 추상 클래스와 인터페이스의 차이점은 무엇인지 확인해보자.

www.tutorialspoint.com/differences-between-abstract-class-and-interface-in-java

 

Differences between abstract class and interface in Java

Differences between abstract class and interface in Java In Java, abstraction is achieved using Abstract classes and interfaces. Both contains abstract methods which a child class or implementing class has to implement. Following are the important differen

www.tutorialspoint.com

키워드 추상 클래스 인터페이스
지원되는 메서드 추상 메서드, 비 추상 메서드 모두 가질 수 있다 추상 메서드만 가질 수 있다. 자바 8부터는 static 메서드를 가질 수 있다
다중 상속 지원되지 않음 지원함
지원되는 변수 final, non-final, static, non-static 변수 모두 지원 static, final 변수만 허용
이행(Implementation) 추상 클래스는 인터페이스를 구현할 수 있다 인터페이스는 인터페이스를 구현할 수 없다. 확장할 수는 있다
선언 키워드 abstract interface
상속 추상 클래스는 extends 키워드를 써서 다른 클래스를 상속하고, 인터페이스를 구현할 수 있다 인터페이스는 인터페이스만 상속할 수 있다
사용 키워드 extends 키워드로 추상 클래스를 상속할 수 있다 implements 키워드로만 구현할 수 있다
접근 제어자 private, public 등 모든 유형의 멤버를 가질 수 있다 public만 가능

 

그럼 추상 클래스, 인터페이스는 각각 언제 쓰면 되는 것인가?

 

www.tutorialspoint.com/when-to-use-an-abstract-class-and-when-to-use-an-interface-in-java

 

When to use an abstract class and when to use an interface in Java?

When to use an abstract class and when to use an interface in Java? An interface can be used to define a contract behavior and it can also act as a contract between two systems to interact while an abstract class is mainly used to define default behavior f

www.tutorialspoint.com

추상 클래스
1. 추상 클래스는 파생 클래스에 공통 기본 클래스 구현을 제공하므로 상속 개념을 사용하는 경우 쓰면 좋다.
2. public이 아닌 멤버를 선언할 경우에도 추상 클래스를 쓰면 좋다.
3. 구성 요소의 여러 버전을 사용하려면 추상 클래스를 쓰는 게 좋다. 추상 클래스를 업데이트하면 추상 클래스를 상속하는 모든 클래스가 변경 사항에 따라 자동으로 업데이트된다. 반면에 인터페이스는 한번 생성하면 변경할 수 없고 새 버전의 인터페이스가 필요하면 새로 만들어야 한다.
4. 구성 요소의 모든 구현에서 공통적인 기능을 쓰려면 추상 클래스를 쓰는 게 좋다. 추상 클래스를 쓰면 클래스의 일부만 구현하는 것이 가능하지만, 인터페이스는 모든 기능을 재정의해서 사용해야 한다.
인터페이스
1. 만들고 있는 기능이 완전히 다른 개체에 쓰여야 할 경우 인터페이스를 쓰는 게 낫다. 추상 클래스는 주로 밀접하게 관련된 객체에 쓰여야 하지만, 인터페이스는 상관없는 클래스에 공통적인 기능을 제공할 때 가장 적합하다.
2. 인터페이스는 API가 한동안 변경되지 않을 거라고 생각할 때 쓰면 좋다.
3. 인터페이스는 여러 인터페이스를 구현(implements)할 수 있어서 다중 상속같은 게 필요할 때 유용하다.
4. 작고 간결한 기능을 디자인하는 경우 인터페이스를 쓰면 좋다. 큰 기능 단위로 디자인하는 경우 추상 클래스를 쓰는 게 낫다.

 

이론적인 걸 보는 것보단 예시 코드로 확인해 보는 게 더 좋을 것 같다.

먼저 추상 클래스를 적용하지 않았을 경우 이런 식으로 클래스를 만들었다고 가정하자.

public class Nasus
{
    // 체력, 공격력
    int hp, attackPoint;

    private void move()
    {
        System.out.println("나서스 - move()");
    }

    private void attack()
    {
        System.out.println("나서스 - attack()");
    }

    private void returnHome()
    {
        System.out.println("나서스 - returnHome()");
    }

    private void furyOfTheSands()
    {
        System.out.println("나서스 - 궁극기 사용");
    }
}

모 인기 게임에 존재하는 캐릭터를 가져왔다.

이와 같이 2개의 캐릭터가 더 존재한다고 가정하고 클래스를 만들어봤다.

public class Nasus
{
    // 체력, 공격력
    int hp, attackPoint;

    private void move()
    {
        System.out.println("나서스 - move()");
    }

    private void attack()
    {
        System.out.println("나서스 - attack()");
    }

    private void returnHome()
    {
        System.out.println("나서스 - returnHome()");
    }

    private void furyOfTheSands()
    {
        System.out.println("나서스 - 궁극기 사용");
    }
}

public class Leona
{
    // 체력, 공격력
    int hp, attackPoint;

    private void move()
    {
        System.out.println("레오나 - move()");
    }

    private void attack()
    {
        System.out.println("레오나 - attack()");
    }

    private void returnHome()
    {
        System.out.println("레오나 - returnHome()");
    }

    private void solarFlare()
    {
        System.out.println("레오나 - 궁극기 사용");
    }
}

public class Lucian
{
    // 체력, 공격력
    int hp, attackPoint;

    private void move()
    {
        System.out.println("루시안 - move()");
    }

    private void attack()
    {
        System.out.println("루시안 - attack()");
    }

    private void returnHome()
    {
        System.out.println("루시안 - returnHome()");
    }

    private void theCulling()
    {
        System.out.println("루시안 - 궁극기 사용");
    }
}

 

나서스, 레오나, 루시안이라는 각 클래스는 체력과 공격력이라는 속성, move / attack / returnHome()이라는 기능을 공통적으로 갖고 있는 걸 볼 수 있다. 그 외에는 클래스 별 다른 궁극기를 갖고 있다는 것 말곤 특별할 게 없는 단순한 클래스들이다.

이 공통적으로 겹치는 요소들을 뽑아서 Champion이라는 추상 클래스로 만든다면 아래와 같이 만들 수 있을 것이다.

abstract class Champion
{
    int hp, attackPoint;
    
    abstract void move();
    
    abstract void returnHome();
    
    void attack()
    {
        // 내용 생략
    }
}

체력과 공격력, 움직이는 기능과 귀환 기능은 세 캐릭터가 모두 공통적으로 수행할 수 있는 기능이지만 기본 공격의 경우 나서스와 레오나는 근접 공격을 하는 반면 루시안은 총으로 원거리 공격을 하기 때문에 공격방법이 달라서 attack()의 상세 구현 내용은 조금 다를 것이다.

이제 이 추상 클래스를 위에서 만든 각 클래스가 상속하도록 하면 아래와 같이 클래스 내 코드가 바뀔 것이다.

public class Nasus extends Champion
{
    // 체력, 공격력
    int hp, attackPoint;

    @Override
    void move()
    {
        System.out.println("나서스 - move()");
    }

    @Override
    void returnHome()
    {
        System.out.println("나서스 - returnHome()");
    }

    public void attack()
    {
        System.out.println("레오나 - attack()");
    }

    private void furyOfTheSands()
    {
        System.out.println("나서스 - 궁극기 사용");
    }

}

public class Leona extends Champion
{
    // 체력, 공격력
    int hp, attackPoint;

    @Override
    void move()
    {
        System.out.println("레오나 - move()");
    }

    @Override
    void returnHome()
    {
        System.out.println("레오나 - returnHome()");
    }

    public void attack()
    {
        System.out.println("레오나 - attack()");
    }

    private void solarFlare()
    {
        System.out.println("레오나 - 궁극기 사용");
    }
}

public class Lucian extends Champion
{
    // 체력, 공격력
    int hp, attackPoint;

    @Override
    void move()
    {
        System.out.println("루시안 - move()");
    }

    @Override
    void returnHome()
    {
        System.out.println("루시안 - returnHome()");
    }

    public void attack()
    {
        System.out.println("루시안 - attack()");
    }

    private void theCulling()
    {
        System.out.println("루시안 - 궁극기 사용");
    }
}

공통된 속성, 기능을 모아둔 추상 클래스를 만들었기 때문에 새로운 챔피언이 추가되더라도 문제없이 기능을 수행할 수 있도록 만들 수 있다.

이제 메인 클래스에서 각 클래스의 객체를 생성한 후 작동시켜보자.

public class Main
{
    public static void main(String[] args)
    {
        Nasus nasus = new Nasus(300, 20);
        nasus.attack();
        nasus.move();
        nasus.furyOfTheSands();
        System.out.println("나서스의 체력 : " + nasus.hp);
        System.out.println("==============================================================");

        Leona leona = new Leona(300, 20);
        leona.attack();
        leona.move();
        leona.solarFlare();
        System.out.println("레오나의 체력 : " + leona.hp);
        System.out.println("==============================================================");

        Lucian lucian = new Lucian(300, 20);
        lucian.attack();
        lucian.move();
        lucian.theCulling();
        System.out.println("루시안의 체력 : " + lucian.hp);
        System.out.println("==============================================================");
    }
}

 

반응형
Comments