article thumbnail image
Published 2020. 4. 4. 15:29

abstract란 한국어로는 추상으로 번역된다.

이에 대한 정의는 뒤에서 내리도록 하고 지금 단계에서는 abstract라는 것이 상속을 강제하는 일종의 규제라고 생각하자.

즉 abstract 클래스나 메소드를 사용하기 위해서는 반드시 상속해서 사용하도록 강제하는 것이 abstract다.

상속해서 사용하도록 강제 !

 사용자가 직접 예쁘게 디자인 해야하는 부분은 abstract로 선언한다. + 상속 강제 

추상 메소드

추상 메소드란 메소드의 시그니처만이 정의된 비어있는 메소드를 의미한다. 아래의 코드를 보자.

package org.opentutorials.javatutorials.abstractclass.example1;
abstract class A{
    public abstract int b();
    //본체가 있는 메소드는 abstract 키워드를 가질 수 없다.
    //public abstract int c(){System.out.println("Hello")}
    //추상 클래스 내에는 추상 메소드가 아닌 메소드가 존재 할 수 있다. 
    public void d(){
        System.out.println("world");
    }
}
public class AbstractDemo {
    public static void main(String[] args) {
        A obj = new A();
    }
}

위 코드의 실행 결과는 아래와 같다.

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
    Cannot instantiate the type A
 
    at org.opentutorials.javatutorials.abstractclass.example1.AbstractDemo.main(AbstractDemo.java:7)

메소드 b의 선언 부분에는 abstract라는 키워드가 등장하고 있다.

이 키워드는 메소드 b는 메소드의 시그니처만 정의 되어 있고 이 메소드의 구체적인 구현은 하위 클래스에서 오버라이딩(자식클래스가 부모 클래스를 변경)해야 한다는 의미다. 이렇게 내용이 비어있는 메소드를 추상 메소드라고 부른다. 추상 메소드하나라도 포함하고 있는 클래스추상 클래스가 되고, 자연스럽게 클래스의 이름 앞에 abstract가 붙는다.

 

아래 코드는 오류를 발생시키는데 본체인 {System.out.println("Hello")}가 존재하는데 추상 메소드를 의미하는 abstract를 사용하고 있기 때문이다.

추상 클래스에는 추상 메소드가 아닌 메소드가 존재할 수 있다.

아래와 같이 추상 클래스 A를 인스턴스화하면 오류가 발생한다.

그것은 추상 클래스는 구체적인 메소드의 내용이 존재하지 않기 때문에 인스턴스화시켜서 사용할 수 없기 때문이다.

 

  1. 즉 abstract 메쏘드는 안에 로직이 있으면 안된다. System.out.println("Hi"); 같은 로직을.
  2. abrstract class 라고 먼저 정의했다기 보다는 b라는 메쏘드가 추상 메쏘드이기 때문에
    b라는 메쏘드를 가지고 있는 클래스 a는 자동으로 추상 클래스가 된다.
  3. abstract 클래스 안에는 구체적인 로직이 있는 일반적인 메쏘드도 가질 수 있다.
    부모클래스 안에 공통적으로 쓰는 메쏘드도 만들 수 있다.

 

그럼 어떻게 해야 클래스 A를 사용할 수 있을까? 또 이렇게 불편한 추상 클래스는 왜 사용하는 것일까?


추상 클래스의 상속

위의 문제를 해결하기 위해서는 클래스 A를 상속한 하위 클래스를 만들고 추상 메소드를 오버라이드해서 내용있는 메소드를 만들어야 한다. 아래 코드를 보자

package org.opentutorials.javatutorials.abstractclass.example2;
abstract class A{
    public abstract int b();
    public void d(){
        System.out.println("world");
    }
}
class B extends A{
    public int b(){return 1;}
}
public class AbstractDemo {
    public static void main(String[] args) {
        B obj = new B();
        System.out.println(obj.b());
    }
}

차이점은 아래와 같다.

클래스 B는 클래스 A를 상속했다.

그리고 클래스 A의 추상 메소드인 메소드 b를 오버라이딩하고 있다.

그 결과 클래스 A를 사용할 수 있었다.


추상 클래스를 사용하는 이유

추상 클래스는 상속을 강제하기 위한 것이다. 즉 부모 클래스에는 메소드의 시그니처만 정의해놓고 그 메소드의 실제 동작 방법은 이 메소드를 상속 받은 하위 클래스의 책임으로 위임하고 있다. 사실 코드를 이런 식으로 작성하는 경우는 작은 규모의 프로젝트에서는 거의 없다. 예제를 통해서 추상 클래스의 용도를 생각해보자.

 

아래 코드는 계산기 예제에 추상 클래스의 개념을 도입한 것이다.

package org.opentutorials.javatutorials.abstractclass.example3;
abstract class Calculator{
    int left, right;
    public void setOprands(int left, int right){
        this.left = left;
        this.right = right;
    } 
    public abstract void sum();  
    public abstract void avg();
    public void run(){
        sum();
        avg();
    }
}
class CalculatorDecoPlus extends Calculator {
    public void sum(){
        System.out.println("+ sum :"+(this.left+this.right));
    }
    public void avg(){
        System.out.println("+ avg :"+(this.left+this.right)/2);
    }
} 
class CalculatorDecoMinus extends Calculator {
    public void sum(){
        System.out.println("- sum :"+(this.left+this.right));
    }
    public void avg(){
        System.out.println("- avg :"+(this.left+this.right)/2);
    }
} 
public class CalculatorDemo {
    public static void main(String[] args) { 
        CalculatorDecoPlus c1 = new CalculatorDecoPlus();
        c1.setOprands(10, 20);
        c1.run();
         
        CalculatorDecoMinus c2 = new CalculatorDecoMinus();
        c2.setOprands(10, 20);
        c2.run();
    }
   
}

사용자에게 구현을 위임하는 것.

사용자가 직접 예쁘게 디자인 해야하는 부분은 abstract로 선언한다. 

run은 부모클래스가 책임을 갖는다. 순차적으로 메쏘드를 실행되게 하는 부분

하위클래스에서 구현하도록 강제하기 위한 기능은 abstract 이다.

 

 

결과는 다음과 같다.

 

위의 예제는 합계(sum)를 실행하고 평균(avg)을 실행하는 절차를 메소드 run을 통해서 한 번에 실행되도록 한 코드이다. 그런데 경우에 따라서 합계와 평균을 화면에 출력하는 모습을 달리해야 하는 경우가 있다고 치자. 그런 경우에 상황에 따라서 동작 방법이 달라지는 메소드(sum, avg)는 추상 메소드로 만들어서 하위 클래스에서 구현하도록 하고 모든 클래스의 공통분모(setOprands, run)의 경우에는 상위 클래스에 두어서 코드의 중복, 유지보수의 편의성 등을 꾀할 수 있다.

 

'📌 java > java' 카테고리의 다른 글

java - 인터페이스  (0) 2020.04.04
java - final  (0) 2020.04.04
java - API  (0) 2020.03.17
java - class, public class 차이  (0) 2020.03.10
java - 패키지  (0) 2020.03.10
복사했습니다!