일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 데이터베이스
- SQL
- 정보처리기사실기
- JSP/Servlet
- 상속
- 컴퓨터구조
- 중학1-1
- JSP
- 파이썬
- 오블완
- 컴퓨터비전
- 정보처리기사필기
- html/css
- 혼공머신
- 자바
- 디버깅
- 자바 실습
- 머신러닝
- 연습문제
- 개발일기
- CSS
- 티스토리챌린지
- 자바스크립트심화
- 자바스크립트
- 데이터분석
- c언어
- 문자와식
- 딥러닝
- JDBC
- 중학수학
- Today
- Total
클라이언트/ 서버/ 엔지니어 "게임 개발자"를 향한 매일의 공부일지
상속 15 - 추상 클래스 본문
추상 클래스는 상속에서 슈퍼 클래스로 사용된다. 지금부터 추상 메서드와 추상 클래스에 대해 알아볼 것이다.
추상 메서드
추상 메서드란 선언은 되어 있으나 코드가 구현되어 있지 않은, 즉 껍데기만 있는 메서드이다. 추상 메서드를 작성하려면 abstract 키워드와 함께 원형만 선언하고 코드는 작성하지 않는다. 다음은 추상 메서드를 선언한 예이다.
public abstract String getName();
public abstract void setName(String s);
다음은 코드가 작성되어 있기 때문에 추상 메서드가 될 수 없다.
public abstract fail() { return "Good Bye"; } // 컴파일 오류
추상 클래스 만들기
추상 클래스가 되는 경우는 다음 2가지로서, 모두 abstract 키워드로 선언해야 한다.
1. 추상 메서드를 포함하는 클래스
abstract class Shape { // 추상 클래스 선언
public Shape() {
public void paint() { draw(); }
abstract public void draw(); // 추상 메서드 선언
}
2. 추상 메서드가 없지만 abstract로 선언한 클래스
abstract class MyComponent { // 추상 클래스 선언
String name;
public void load(String name) {
this.name = name;
}
}
앞의 Shape과 MyComponent 모두 추상 클래스이다. Shape은 추상 메서드를 가진 추상 클래스이며, MyComponent는 추상 메서드 없는 추상 클래스이다.
추상 메서드를 가지고 있으면 반드시 추상 클래스로 선언되어야 한다. 다음은 추상 클래스로 선언되지 않는 잘못된 코드이다.
class Fault { // 오류. 추상 클래스로 선언되지 않았음
abstract public void f(); // 추상 클래스
}
추상 클래스는 객체를 생성할 수 없다
추상 클래스는 본래 객체를 생성할 목적으로 만드는 클래스가 아니다. 추상 클래스에는 실행 코드가 없는 미완성 상태인 추상 메서드가 있을 수 있기 때문에 다음과 같이 추상 클래스의 객체를 생성하는 코드에는 컴파일 오류가 발생한다.
public class AbstractError {
public static void main(String[] args) {
Shape shape;
shape = new Shape(); // 컴파일 오류. 추상 클래스 Shape의 객체를 생성할 수 없음
...
}
}
이클립스에서 위의 코드를 컴파일하면 다음과 같은 오류가 발생한다.
이 오류는 추상 클래스는 객체를 생성할 수 없다는 뜻이다. 앞의 코드를 자세히 보면 main()의 다음 코드에는 오류가 발생하지 않는다.
Shape shape; // 오류 아님
추상 클래스의 레퍼런스 변수를 선언하는 것은 오류가 아니다.
추상 클래스의 상속
추상 클래스를 단순히 상복받는 서브 클래스는 추상 클래스가 된다. 추상 클래스의 추상 메서드를 그대로 상속받기 때문이다. 다음과 같이 추상 클래스 Shape을 상속받는 Line 클래스에서 추상 메서드인 draw()를 오버라이딩하지 않으면 자동으로 추상 클래스가 되므로, Line은 abstract 키워드를 사용하여 추상 클래스임을 명시해야 한다.
abstract class Shape { // 추상 클래스
public Shape() { }
public void paint() { draw(); }
abstract public void draw(); // 추상 메서드
}
abstract class Line extends Shape { // 추상 클래스. draw()를 상속받기 때문
public String toString() { return "Line"; }
}
추상 클래스의 구현과 목적
추상 클래스의 구현이란 슈퍼 클래스에 선언된 모든 추상 메서드를 서브 클래스에서 오버라이딩하여 실행 가능한 코드로 구현하는 것을 말한다. 아래 그림은 그림 5-19의 코드에서 Shape을 추상 클래스로 수정하고, draw()를 추상 메서드로 수정하였다. Shape을 추상 클래스로 바꾸어도 나머지 Line, Rect, Circle 클래스를 수정할 필요는 없다.
그러면 이 두 중 어느 것이 더 바람직할까? 그림 5-19에서는 Shape 클래스의 draw() 메서드가 추상 메서드가 아니므로, Line, Rect, Circle 클래스에서 draw()를 오버라이딩하지 않아도 무관하다. 혹은 drow()라고 잘못 코딩하여도 컴파일 오류가 없다.
하지만 그림 5-25에서는 Shape의 draw() 메서드가 추상 메서드이기 때문에 이를 상속받는 Line, Rect, Circle 클래스에서는 반드시 draw()를 오버라이딩해야 한다. 오버라이딩하지 않거나 이름을 잘못 작성하여 drow()라고 한다면 컴파일 오류가 발생한다.
추상클래스는 추상 메서드를 통해 서브 클래스가 구현할 메서드를 명료하게 알려주는 인터페이스 역할을 하고, 서브 클래스는 추상 메서드를 목적에 맡게 구현하는 다형성을 실현할 수 있다.
추상 클래스의 용도
서브 클래스에서 추상 클래스에 선언된 추상 메서드를 모두 구현해야 한다. 추상 클래스를 책의 목차에 비유하며, 서브 클래스는 목차에 따라 작성된 척과 같다. 책을 쓸 때도 목차를 잡아놓고 책을 쓰면 훨씬 쉽고 빠르며 방향이 흐트러지지 않는 것처럼, 추상 클래스는 이용하면 응용프로그램의 설계와 구현을 분리할 수 있다.
추상 클래스로 기본 방향을 잡아놓고 서브 클래스에서 구현하면 구현 작업이 쉬워진다. 또한 추상 클래스는 계층적 상속 관계를 가지는 클래스들의 구조를 만들 때 적합하다.
추상 클래스의 구현 연습
다음 추상 클래스 Calculator를 상속받은 GoodCalc 클래스를 구현하자.
추상 클래스인 Calculator는 add(), subtract(), average() 메서드를 추상 클래스로 선언하였을 뿐 어떻게 구현할지는 지시하지 않는다. 어떤 인자가 전달되고 어떤 값이 리턴되는지만 지정할 뿐이다. 구현은 서브 클래스의 몫이다.
핵심 체크문제 풀기
이 문제도 책의 정답은 틀리게 말한 것 같다. 내 나름대로 풀이를 진행해보려고 한다.
문제 1번
(1)번 문제
이처럼 오류가 발생한다.
문제의 원인
- 미완성 메서드 정의
- 메서드 f()가 미완성 상태로 선언되었지만, 클래스 A는 일반 클래스이다.
- 일반 클래스에서는 구현부가 없는 메서드(추상 메서드)를 선언할 수 없다.
- 구현부가 없는 메서드를 선언하려면, 반드시 클래스를 추상 클래스(abstract class)로 선언해야 한다.
- 추상 메서드와 일반 클래스
- 일반 클래스에서는 반드시 모든 메서드에 구현부(중괄호 {})가 있어야 한다.
- 위 코드에서는 구현부가 없기 때문에 오류가 발생한다.
올바른 코드 수정
수정 방법 1: 클래스 A를 추상 클래스로 변경
- 클래스 A를 추상 클래스로 선언하면, 구현부가 없는 메서드(f())를 선언할 수 있다.
- 추상 클래스는 객체를 생성할 수 없으며, 이 클래스를 상속받는 자식 클래스에서 f() 메서드를 구현해야 한다.
수정 방법 2: 메서드 f()에 구현부 추가
- f() 메서드에 구현부를 추가하면, 일반 클래스 A에서 메서드를 사용할 수 있다.
(2)번 문제
원래는 class 이름이 A이지만 같은 패키지에 동일한 클래스 이름을 사용할 수 없기에 C로 변경해 보았다. 이 문제에서는 오류가 없다. 추상 클래스 C는 추상 메서드가 없고 일반 메서드(void f() {})만 포함하고 있다. 추상 클래스는 객체를 직접 생성할 수 없지만, 추상 메서드가 없는 경우 굳이 추상 클래스로 선언할 필요가 없다.
(3)번 문제
문제점
- 추상 클래스의 객체 생성 문제
- D d = new D();는 오류를 발생한다.
- 추상 클래스는 객체를 생성할 수 없다.
- 추상 클래스는 인스턴스를 생성하지 못하도록 설계된 클래스이기 때문이다.
- 자식 클래스의 오버라이딩
- B 클래스에서 f() 메서드를 오버라이딩하고 있지만, 부모 클래스의 f() 메서드와 동작이 크게 다르지 않다.
수정 방법 : 추상 클래스 A의 객체 생성 문제 해결
- 추상 클래스는 인스턴스를 생성할 수 없으므로, A의 객체를 생성하지 않도록 코드를 수정해야 한다.
- 다형성을 활용하여 자식 클래스 B의 객체를 생성하도록 수정한다.
학습을 마치고
핵심 체크 문제를 푸느라 시간이 많이 걸렸다. 그리고 이 학습은 절반 정도 진행하다 새벽 1시 20분쯤 너무 졸려서 잠을 자고 3시에 일어나서 다시 공부를 진행했다. 추상 메서드에 대해서 많은 것들을 이해할 수 있는 단원이었다. 처음에는 귀찮아서 스캔을 주로 받으려고 했으나 그래도 쓰면서 공부가 많이 되니 코드를 치면서 기록해 보았다.
이제 인터페이스만 학습하면 상속에 대한 개념은 마칠 것 같다.
'프로그래밍 언어 > 자바' 카테고리의 다른 글
상속 17 - 인터페이스 2 : 다중 인터페이스 구현과 추상 클래스 vs 인터페이스 비교하기 (0) | 2024.12.02 |
---|---|
상속 16 - 인터페이스 1 : 인터페이스의 개념과 구현 방법 그리고 인터페이스 상속에 대하여 (0) | 2024.12.02 |
상속 14 - 메서드 오버라이딩 2 : 동적 바인딩과 오버로딩 vs 오버라이딩 (0) | 2024.12.01 |
상속 13 - 메서드 오버라이딩 1 : 메서드 오버라이딩의 개념 및 활용 예시 (0) | 2024.12.01 |
상속 12 - 업 캐스팅과 instanceof 연산자 (0) | 2024.12.01 |