자바 Java

추상 클래스(abstract class) 와 인터페이스(interface) / 확장 / 예시 / 용도의 차이

leexx 2023. 9. 28. 21:44

추상 클래스와 인터페이스

abstract class 와 interface class 는 둘 다 유사 개념에 대해 공통 코드를 만들 수 있고, 인스턴스를 바로 만들 수 없는 설계도 라는 공통점을 갖고 있다. 그렇다면 어떤 차이점을 갖고 있을까?

 

추상 클래스 

abstract class (추상 클래스) 는 상속 (extends) 을 통해 구현될 수 있는 클래스 이다. 상속은 단일 상속 만 가능하다. 

추상클래스는 IS-A 관계로 '무엇' 을 의미한다. 즉 무엇인가 가 어떤 특성(멤버 변수) 과 행동(메서드) 을 가지는 것을 정의할 수 있다. 따라서 멤버변수와 메서드를 구현할 수 있다. 

 

추상 클래스의 확장 

따라서 추상 클래스의 확장은, 특성이나 행동이 확장되는 것을 의미한다. 

abstract class Animal {
    abstract void eat();
    
    void breathe() {
        System.out.println("Breathing");
    }
}

abstract class Bird extends Animal {
    void fly() {
        System.out.println("Flying");
    }
}

이 경우 Animal 에서 Bird 로 확장되면서, fly 라는 행동이 추가되었다. 

 

 

그리고 다수의 interface 와 abtract class 의 상속이 가능하다. 

package my_package;

abstract public class OtherAbstractC extends MyAbstractC implements MyInterface, OtherInterface {
}

 

인터페이스 

inteface (인터페이스) 는 이름에서 처럼, 클래스는 아니다. implements 를 통해 구현 될 수 있고, 다중 상속 이 가능하다.

인터페이스는 추상 클래스보다 더 추상화 된 것으로, 상수와 메서드 선언부 만 가질 수 있다. 

추상클래스는 HAS-A 관계로 '할 수 있는' 을 의미한다. 따라서 인터페이스의 이름은 ~able 로 끝난다. (ex. Runnable) 

 

인터페이스의 확장 

interface Readable {
    void read();
}

interface Writable extends Readable {
    void write();
}

interface Editable extends Writable {
    void edit();
}

class Document implements Editable {
    @Override
    public void read() {
        System.out.println("Reading the document.");
    }

    @Override
    public void write() {
        System.out.println("Writing to the document.");
    }

    @Override
    public void edit() {
        System.out.println("Editing the document.");
    }
}

public class InterfaceExample {
    public static void main(String[] args) {
        Document doc = new Document();
        doc.read();   // "Reading the document."
        doc.write();  // "Writing to the document."
        doc.edit();   // "Editing the document."
    }
}

읽을 수 있으면(Readable) 쓸 수 있고(Writable), 쓸 수 있으면 편집 할 수(Editable) 있다. 즉 확장될 수록 할 수 있는 것 이 많아진다. 

Document 는 Editable 을 implements 하고 있으므로, 읽고 쓰고 편집할 수 있다. 

 

HAS-A, IS-A 의 관계

출처는 quora (클릭 시 이동)

 

 

IS-A, HAS-A 가 헷갈려서 찾아봤다. 

그림처럼, IS-A 는 '무엇, 어떤 것' 을 나타낸다. 즉 자동차는 탈 것 이다.

HAS-A 는 '~를 할 수 있는' 을 나타낸다. 그런데 그림에서는 약간 헷갈릴 수 있다. 자동차는 엔진을 할 수 있는... 이 무슨 뜻이지... 라고 생각할 수 있다. 여기서는 차는 엔진을 가동할 수 있는 을 의미한다.

 

각각의 용도

  • 추상클래스 implements 라는 키워드 처럼, 정의된 함수를 목적에 맞게 구현 하는 것을 말한다.
  • 인터페이스 extends 라는 키워드 처럼, 자신의 기능들을 확장 시키는 것을 말한다.

(출처는 여기)

 

예시로 알아보는 추상클래스와 인터페이스 

추상클래스는 어떤 것을, 인터페이스는 어떤 능력을 의미한다. 

 

FE 개발자로 예시를 알아보자. (Abstract class 와 Interface 에 대한 글인데 FE 개발자인 이유는... 제가 FE 개발자 이기 때문입니다아.. 😌 그리고 아래 예시는 예시가 그렇다... 는거지 꼭 저렇지많은 않습니다 😌...)

 

FE 개발자는 개발자이다. FE 개발자는 HTML과 SCSS, TypeScript 를 다룰 줄 알아야 한다. SCSS 는 CSS 를 알아야 하고, TypeScript 는 JavaScript 를 알아야 한다. 

FE 개발자이며 React 를 다룰 줄 알면 React 개발자가, Angular 를 다룰 줄 알면 Angular 개발자가, Vue 를 다룰 줄 알면 Vue 개발자가 된다. Jerry, James, Jenny 는 각각 React, Angular, Vue 개발자 이다. 

 

이를 도식으로 그려보면 이렇다. 

예시 코드 

더보기
package fe_developer;

interface JavaScriptUsable {
    void writeJavaScriptCode();
}
interface TypeScriptUsable extends JavaScriptUsable {
    void writeTypeScriptCode();
}

interface CSSUsable {
    void writeCSSCode();
}
interface SCSSUsable extends CSSUsable {
    void writeSCSSCode();
}

interface HTMLUsable {
    void writeHTMLCode();
}


interface ReactUsable {
    void writeReactCode();
}
interface AngularUsable {
    void writeAngularCode();
}
interface VueUsable {
    void writeVueCode();
}

abstract class Developer {
    abstract void writeHelloWorld();
}
class FEDeveloper extends Developer implements TypeScriptUsable, SCSSUsable, HTMLUsable {

    @Override
    public void writeJavaScriptCode() {
        System.out.println("Write Clean JavaScript Code");
    }

    @Override
    public void writeTypeScriptCode() {
        System.out.println("Write Clean TypeScript Code");
    }

    @Override
    public void writeCSSCode() {
        System.out.println("Write Clean CSS Code");
    }

    @Override
    public void writeSCSSCode() {
        System.out.println("Write Clean SCSS Code");
    }

    @Override
    public void writeHTMLCode() {
        System.out.println("Write Clean HTML Code");
    }

    @Override
    void writeHelloWorld() {
        System.out.println("HelloWorld");
    }
}

class ReactDeveloper extends FEDeveloper implements ReactUsable {
    @Override
    public void writeReactCode() {
        System.out.println("Write Clean React Code");
    }
};

class AngularDeveloper extends FEDeveloper implements AngularUsable {

    @Override
    public void writeAngularCode() {
        System.out.println("Write Clean Angular Code");
    }
}

class VueDeveloper extends FEDeveloper implements VueUsable {

    @Override
    public void writeVueCode() {
        System.out.println("Write Clean Vue Code");
    }
}


public class Main {
    public static void main(String[] args) {
        ReactDeveloper james = new ReactDeveloper();
        james.writeHelloWorld();
        james.writeTypeScriptCode();
        james.writeReactCode();

        AngularDeveloper jerry = new AngularDeveloper();
        jerry.writeHelloWorld();
        jerry.writeSCSSCode();
        jerry.writeCSSCode();
        jerry.writeAngularCode();

        VueDeveloper jenny = new VueDeveloper();
        jenny.writeHelloWorld();
        jenny.writeHTMLCode();
        jenny.writeVueCode();
    }
}

 

출력 결과

더보기
// James
HelloWorld
Write Clean TypeScript Code
Write Clean React Code
// Jerry
HelloWorld
Write Clean SCSS Code
Write Clean CSS Code
Write Clean Angular Code
// Jenny
HelloWorld
Write Clean HTML Code
Write Clean Vue Code