반응형

1. 개요

 추상클래스와 인터페이스에 대해 알아보자.


2. 추상클래스란?

 - 추상(abstract) 클래스란 추상 메서드를 포함한 클래스이다.

 - 추상(abstract) 메서드란 선언만 있고, 구현이 없는 메서드이다.

 - 추상 메서드가 하나라도 선언되어있으면 객체를 만들 수 없기때문에 서브클래스를 만드는 용도로 사용된다.

 - 추상 메서드와 추상 클래스는 abstract 키워드로 표시한다.

 - 추상 클래스를 상속받는 클래스는 추상메서드를 구현해야한다.


3. 추상 클래스 사용 이유?

 객체도 못만들고, 번거롭게 abstract 키워드 추가하고, 구현하지 않는 추상메서드를 굳이 선언... 왜 쓸까?

 라는 의문이 들기 마련이다. 말보다는 직접 코드로 경험하면 이해할 수 있을 것이다. 다음 상황을 생각해보자.


4. 상황

 찰흙을 빚어 도형을 만들고, 만든 도형을 전시하는 프로그램을 구현한다. 이에 대해 요구사항을 부여하였다.

 1) 삼각형, 사각형, 원 모양의 도형을 만들 수 있다.

 2) 내가 만든 도형에 대해 관리 및 조회할 수 있다.

 3) 도형 전시를 위해 해당 도형의 넓이를 알아야 한다.

 

 직접 코딩을 하기 전에 생각해보자. 

 1번을 처리하기 위해서는 삼각형, 사각형, 원에 대한 클래스가 필요하다.

 2번을 처리하기 위해서는 내가 만든 도형을 배열이나 리스트로 관리해야한다. 삼각형 배열에는 삼각형 객체를, 사각형 배열에는 사각형 객체를, 원 배열에는 원 객체를 넣어 관리해도되지만, 3개의 배열을 각각 선언해야 하니 효율적으로 느껴지진 않는다. 그래서 도형이라는 클래스를 만들고 삼각형, 사각형, 원 클래스가 이를 상속하도록 구현한다. 이렇게 되면 도형이라는 자료형 하나로 모든 클래스를 관리할 수 있다.

 3번을 처리하기 위해서는 각 클래스마다 자신의 넓이를 구하는 메서드가 필요하다.

 이제 직접 예제를 통해 이를 구현해보겠다.


5. 예제

  

5.1. 클래스 생성

 - Triangle, Square, Circle, Figure(도형), Exhibition(전시), Main 클래스를 생성한다.

 - Triangle, Square, Circle은 Figure 클래스를 상속받는다.

 - 삼각형은 밑변, 높이. 사각형은 가로, 세로. 원은 반지름 값을 나타내는 멤버변수를 정의하고, 생성자를 생성한다.

 

5.2. 1번 요건 처리

 - 세 클래스를 다음과 같이 구현한다.

 - 클래스를 구현했으니 각 도형에 대한 객체를 만들 수 있게 되었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class Triangle extends Figure{
 
    private int base;
    private int height;
    
    public Triangle( int baseint height) {
        this.base = base;
        this.height = height;
    }
}
 
 
/////////////////////////////////////
 
public class Square extends Figure{
 
    private int horizontal;
    private int vertical;
    
    public Square( int horizontal, int vertical) {
        this.horizontal = horizontal;
        this.vertical = vertical;
    }
}
 
/////////////////////////////////////
 
public class Circle extends Figure{
 
    private int redius;
    
    public Circle(int redius) {
        this.redius = redius;
    }
}
cs

 

5.3. 2번 요건 처리

 - 조회에 사용할 toString 메서드를 각 클래스에 추가한다.

 - 도형들을 관리할 Exhibition 클래스를 정의한다.

 

Trigangle.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Triangle extends Figure{
 
    private int base;
    private int height;
    
    public Triangle( int base, int height) {
        this.base = base;
        this.height = height;
    }
    
    public String toString() {
        return "class : Triangle , base : "+base + ", height : "+height; 
    }
}
cs

 

Square.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Square extends Figure{
 
    private int horizontal;
    private int vertical;
    
    public Square( int horizontal, int vertical) {
        this.horizontal = horizontal;
        this.vertical = vertical;
    }
    
    public String toString() {
        return "class : Square , horizontal : "+horizontal + ", vertical : "+vertical; 
    }
}
cs

 

Circle.java

1
2
3
4
5
6
7
8
9
10
11
12
public class Circle extends Figure{
 
    private int redius;
    
    public Circle(int redius) {
        this.redius = redius;
    }
    
    public String toString() {
        return "class : Circle , redius : "+redius; 
    }
}
cs

 

Exhibition.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Exhibition {
 
    private List<Figure> list;
    
    public void setList( List<Figure> list ) {
        this.list = list;
    }
    
    public void showList() {
        for(Figure figure : list) {
            System.out.println(figure.toString());
        }
    }
}
cs

 

이제 Main 클래스에 main 메서드를 생성하여 1,2 번 요건이 만족되도록 정의 후 실행시켜보자.

 

Main.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Main {
 
    public static void main(String[] args) {
        //삼각형, 사각형, 원 객체 생성
        Triangle triangle = new Triangle(4,3);
        Square square = new Square(5,5);
        Circle circle = new Circle(10);
                
        // 관리에 사용될 리스트 생성 및 추가
        List<Figure> list = new ArrayList<Figure>();
                
        list.add(triangle);
        list.add(square);
        list.add(circle);
                
        Exhibition exhibition = new Exhibition();
        exhibition.setList(list);
        exhibition.showList();
                
    }
}
cs

5~7번 라인에서 삼각형, 사각형 원 객체를 만든다.

10번 라인에서 도형을 관리할 list를 생성한다.

12~14번 라인에서 생성한 도형들을 list에 넣는다.

16~17번 라인에서 전시 객체를 생성하고 그 안에 만든 도형들을 set 한다.

18번 라인에서 도형들을 조회한다.

 

출력 결과를 확인해보면 다음과 같이 조회된다.

showList()

 

이제 마지막 요건인 도형의 넓이를 구하면 끝이다. 그리고 여기서 추상 클래스의 사용 이유에 대해 조금은 와닿을 수 있을 것이다.

 

5.4 3번 요건 처리

 - 각 도형마다 구하는 넓이 공식이 다르기때문에 삼각형, 사각형, 원 클래스에 넓이를 구하는 메서드를 각각 구현한다.

 - Exhibition 클래스에서는 구현한 메서드를 출력하는 메서드를 생성한다.

 

Triangle.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Triangle extends Figure{
 
    private int base;
    private int height;
    
    public Triangle( int base, int height) {
        this.base = base;
        this.height = height;
    }
    
    public String toString() {
        return "class : Triangle , base : "+base + ", height : "+height; 
    }
    
    public double getArea() {
        return base * height * 0.5;
    }
}
cs

 

Square.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Square extends Figure{
 
    private int horizontal;
    private int vertical;
    
    public Square( int horizontal, int vertical) {
        this.horizontal = horizontal;
        this.vertical = vertical;
    }
    
    public String toString() {
        return "class : Square , horizontal : "+horizontal + ", vertical : "+vertical; 
    }
    
    public double getArea() {
        return horizontal * vertical;
    }
}
cs

 

Circle.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Circle extends Figure{
 
    private int redius;
    
    public Circle(int redius) {
        this.redius = redius;
    }
    
    public String toString() {
        return "class : Circle , redius : "+redius; 
    }
    
    public double getArea() {
        //파이 값은 임의로 3.14로 가정한다.
        return redius * redius * 3.14;
    }
}
cs

 

Exhibition.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Exhibition {
 
    private List<Figure> list;
    
    public void setList( List<Figure> list ) {
        this.list = list;
    }
    
    public void showList() {
        for(Figure figure : list) {
            System.out.println(figure.toString());
        }
    }
    
    public void getFigureArea() {
        for(Figure figure : list) {
            //컴파일 발생
            System.out.println(figure.getClass().getSimpleName()+ " : "+ figure.getArea());
        }
    }
}
cs

 

 이로써 넓이를 구하는 것까지 구현했다고 생각했으나, Exhibition.java 코드의 18번째 라인에서 에러가 발생하게 된다.

발생 원인은 figure 객체에 getArea() 메서드를 찾지 못해서 발생한다.

하지만 분명 figure는 삼각형, 사각형, 원 클래스 중 한 객체를 참조하고 있고, getArea 메서드가 정의되어 있는데 왜 찾지 못하는 걸까? 바로 다이나믹 바인딩의 처리가 런타임시점에서 진행되기 때문이다.

 실제 코드를 돌리면 16번째의 figure 객체는 삼각형, 사각형, 원 객체 중 하나겠지만, 컴파일러 입장에서는 컴파일 시점에 이를 확인할 방법이 없기 때문에 에러가 뜨는것이다.

 

그럼 해결방안으로 Figure 클래스에 getArea 메서드를 생성하는 방법이 있다.

그렇게 되면 컴파일 시점에는 에러가 발생한 지점에서 getArea 메서드를 Figure 클래스의 메서드로 인식하여 에러를 떨구지 않을 것이고, 런타임 시에는 다이나믹 바인딩으로 인해 참조 객체의 getArea 메서드를 호출할 것이다.

 

하지만 Figure 클래스의 getArea가 전혀 사용되지 않는 상황인데 저렇게 메서드를 정의해두면, 불필요한 코드일 뿐더러, 타 개발자가 이를 봤을 때 당연히 이 메서드와 클래스가 어디서 사용되고 있을 것이라고 생각할 수 있다. 하지만 코드를 보면 실제로 Figure 클래스는 참조객체로써 사용되지 않고있다. 뭔가 혼란스럽지 않는가? 이러한 혼란 해소할 수 있는 것이 바로 추상 메서드, 추상 클래스이다.

 

 추상 메서드를 사용했기 때문에 Figure 클래스는 getArea 메서드를 선언만 해놓으면 된다. 그럼 타 개발자가 이 코드를 봤을 때 이 클래스가 객체로 생성되어 어디선가 사용되고 있지 않을까에 대해 생각할 필요가 없다. 개요에서 설명했듯이 추상클래스는 서브클래스를 위한 클래스이고, 객체로 생성이 불가능하기 때문이다. 또한 자식 클래스에서 정의해서 쓰고 있다라는 사실을 명확하게 알 수 있다.

 

Figure.java

1
2
3
4
5
public abstract class Figure {
 
    public abstract double getArea();
}
 
cs

 

실행화면

 

실행 결과를 통해 모든 요건을 만족하는것을 확인할 수 있다.


 

 

설명이 좀 중구난방 한것같지만 그래도 쥐어짜면서 글을 써내려가니 머릿속에서 확실히 조금 더 정리된 느낌이고, 이전에 공부했던 정형화나 다이나믹 바인딩도 복습할 수 있었다.

반응형

+ Recent posts