Java 기본 과정

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가 붙는다. 
 
추상 클래스 A를 인스턴스화하면 오류가 발생한다. 그것은 추상 클래스는 구체적인 메소드의 내용이 존재하지 않기 때문에 인스턴스화시켜서 사용할 수 없기 때문이다. 그럼 어떻게 해야 클래스 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());
    }
}
 
추상 클래스를 사용하는 이유
 
추상 클래스는 상속을 강제하기 위한 것이다. 즉 부모 클래스에는 메소드의 시그니처만 정의해놓고 그 메소드의 실제 동작 방법은 이 메소드를 상속 받은 하위 클래스의 책임으로 위임하고 있다. 사실 코드를 이런 식으로 작성하는 경우는 작은 규모의 프로젝트에서는 거의 없다. 예제를 통해서 추상 클래스의 용도를 생각해보자.
 
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();
    }
   
}
 
결과는 다음과 같다.
+ sum :30
+ avg :15
- sum :30
- avg :15
 
경우에 따라서 합계와 평균을 화면에 출력하는 모습을 달리해야 하는 경우가 있다고 치자. 그런 경우에 상황에 따라서 동작 방법이 달라지는 메소드(sum, avg)는 추상 메소드로 만들어서 하위 클래스에서 구현하도록 하고 모든 클래스의 공통분모(setOprands, run)의 경우에는 상위 클래스에 두어서 코드의 중복, 유지보수의 편의성 등을 꾀할 수 있다.
​추상클래스를 사용하지 않는 경우에는 메소드 sum()과 avg()의 구현을 강제할 수 없을 것이다.
 
 

댓글

댓글 본문