추상메서드와 추상클래스 (Abstract)
목차
EX3 ) 서브클래스에서 추상메서드의 오버라이딩 분할하기
📍 추상메서드 (Abstract Method)
– 메서드 바디(= 구현부 { })가 존재하지 않는 메서드 = 미완성 메서드
→ 실행 코드가 없으므로 실행 (호출)될 수 없는 메서드.
– 메서드 선언부 접근제한자 뒤에 abstract 키워드를 붙여서 선언.
→ 바디가 없으므로 메서드 마지막을 세미콜론(;)으로 마무리 함.
→ 메서드 실행 코드(바디)가 없으므로 외부에서 호출되면 안되는 메서드.
– 바디가 없을 뿐, 파라미터로 값 전달 받을 수 있음.
▸ 추상메서드 정의 기본 문법
[접근제한자] abstract 리턴타입 메서드명 ( [매개변수...] );
▸ 추상 메서드를 선언하는 이유
– 특정 메서드를 각 클래스 별로 재 구현 해야 하는데,
부모 클래스에서 일반 메서드로 구현하면 자식 클래스에서 구현을 하지 않는 경우가 발생할 수 있음.
→ 이런 메서드를 추상 메서드로 선언하면 자식 클래스는 무조건 재 구현을 해야 하는 강제성이 부여됨.
📍 추상클래스 (Abstract Class)
– 인스턴스를 생성할 수 없는 '미완성 클래스'.
→ 내부에 추상메서드를 가지고 있을 경우 추상메서드가 호출되면 안되므로 인스턴스 생성을 못하게 차단함.
– class 키워드 앞에 abstract 키워드를 붙여서 정의.
– 추상메서드 뿐만 아니라 일반메서드, 멤버변수, 생성자를 가질 수 있음.
→ 추상메서드가 없는 메서드도 추상클래스로 정의할 수 있음.
– 인스턴스 생성 불가지만 상속을 통한 슈퍼클래스로 사용하거나, 다형성 활용을 위한 참조변수 타입으로 사용 가능함.
– 추상메서드를 포함하는 추상클래스를 상속받는 서브클래스에서는
반드시 오버라이딩을 통해 추상메서드 바디{ }를 구현해야 함. 즉, 추상메서드 오버라이딩을 강제할 수 있음.
(what에 대한 강제, how는 서브클래스에게 위임)
– 추상메서드에 대한 구현을 강제함으로써 코드의 강제성 및 통일성 향상.
– 추상메서드를 갖는 클래스는 반드시 추상클래스로 선언되어야 함.
▸ 추상클래스 정의 기본 문법
[접근제한자] abstract class 클래스명 {
멤버변수
생성자
일반메서드
추상메서드
}
▸ 추상 클래스의 장점
1 ) 부모클래스에서 공통 부분의 구현과 설계가 완료되면, 자식 클래스에서 상속받아 기능을 확장할 시 이로움.
2 ) 자식 클래스에서 추상메서드를 반드시 구현하도록 강요하므로 프로그램의 표준화 정도를 높임.
3 ) 공통 사항이 한곳에서 관리되어 개발 및 유지보수에 용이함.
EX1 ) 기본 정의 및 사용 |
• 추상클래스 정의
abstract class AbstractClass {
// public abstract void abstractMethod() {}
public abstract void abstractMethod();
// 추상클래스가 추가로 가질 수 있는 것
public void normalMethod() {} // 일반 메서드
public AbstractClass() {} // 생성자
String member; // 멤버변수(=인스턴스 변수)
}
코드 분석
3 | Abstract methods do not specify a body 에러. 추상메서드는 바디부분이 없어야 하므로 추상메서드 정의시 반드시 중괄호를 제거해야 함. |
4 | 추상메서드를 갖는 클래스는 반드시 추상클래스로 선언되어야 하므로 class 키워드 앞에 abstract 키워드 필수. |
• 추상클래스 AbstractClass를 상속받는 서브클래스 SubClass 정의
class SubClass extends AbstractClass{
@Override
public void abstractMethod() {
System.out.println("서브클래스에서 오버라이딩(구현) 된 추상메서드");
}
public void subClassMethod() {
System.out.println("서브클래스에서 정의한 메서드");
}
}
– SubClass 유형은 상속된 추상 메서드를 구현해야 함.
→ 추상클래스를 상속받은 서브클래스는 반드시 추상메서드 오버라이딩 필수 !
→ 바디를 갖지 않는 추상메서드 바디를 구현(implement) 하는 작업.
• main 메서드에서 실행
// 추상클래스 AbstractClass 인스턴스 생성
// AbstractClass ac = new AbstractClass(); // 에러 발생
// 추상클래스를 상속받은 서브클래스 인스턴스 생성
SubClass sc = new SubClass();
sc.normalMethod();
sc.abstractMethod();
sc.subClassMethod();
// 모두 접근 가능
// 업캐스팅
AbstractClass ac = new SubClass();
ac.normalMethod(); // 공통(상속된) 메서드
ac.abstractMethod(); // 공통(상속된) 메서드
// → 오버라이딩 메서드 → 부모의 꿈을 자식이 이룸
// 호출 불가
//ac.subClassMethod();
코드 분석
2 | – 추상클래스는 인스턴스 생성 불가하므로 에러 발생. |
5~9 | – 추상클래스를 상속받은 서브클래스는 인스턴스 생성이 가능함. → 추상메서드를 반드시 오버라이딩하여 바디를 구현하기 때문에 메서드 호출이 가능하기 때문. – 상속관계이므로 추상클래스의 메서드와 자신의 메서드 모두 접근 가능함. |
14 | – 업캐스팅을 통한 다형성. – 추상클래스를 참조변수 타입으로 활용 가능. |
16 | – 공통 (상속된)메서드 → 오버라이딩 메서드 → 부모의 꿈을 자식이 이룸 → 추상클래스로 부터 상속 받은 추상메서드가 서브클래스에서 오버라이딩 하여 바디를 구현함. → 부모가 못 다 이룬 꿈 (추상메서드 바디구현)을 자식이 대신 (오버라이딩 하여) 이루어줌. |
20 | – 서브클래스에서 정의한 메서드 (멤버)이므로 업캐스팅 후 호출 불가. → 참조 영역 축소로 보이지 않는 메서드. → 접근하려면 다시 다운캐스팅 해야 함. |
EX2 ) 다형성 활용하기 |
• 슈퍼클래스 정의
– 슈퍼맨, 새, 비행기의 공통점 = 비행 가능.
– 공통점을 추출하여 상위클래스로 정의하되, 각 서브클래스에서 비행 기능을 반드시 구현하도록 강제성을 부여.
abstract class Flyer{
public abstract void takeOff();
public abstract void fly();
public abstract void land();
}
– 모든 날아다니는 것들에 대한 이름, 비행, 착륙 메서드 정의.
– 슈퍼클래스로 사용할 Flyer 클래스의 메서드들에는 실행할 코드가 불필요 (바디 불필요)하므로 추상메서드로 정의하고,
추상메서드를 포함하는 Flyer 클래스를 추상클래스로 정의.
• SuperMan 클래스 정의 — Flyer 상속
class SuperMan extends Flyer{
@Override
public void takeOff() {
System.out.println("SuperMan 이륙");
}
@Override
public void fly() {
System.out.println("SuperMan 비행");
}
@Override
public void land() {
System.out.println("SuperMan 착륙");
}
}
– 슈퍼클래스 Flyer의 추상메서드 3개를 반드시 오버라이딩 해야 함.
→ 오버라이딩을 통해 실행 코드를 작성, 즉 바디를 구현함.
→ 이렇게 오버라이딩 된 메서드는 이제 호출 가능.
• Bird 클래스 정의 — Flyer 상속
class Bird extends Flyer{
@Override
public void takeOff() {
System.out.println("Bird 이륙");
}
@Override
public void fly() {
System.out.println("Bird 비행");
}
@Override
public void land() {
System.out.println("Bird 착륙");
}
}
• AirPlane 클래스 정의 — Flyer 상속
class AirPlane extends Flyer{
@Override
public void takeOff() {
System.out.println("AirPlane 이륙");
}
@Override
public void fly() {
System.out.println("AirPlane 비행");
}
@Override
public void land() {
System.out.println("AirPlane 착륙");
}
}
• 서브클래스의 인스턴스 생성
SuperMan s = new SuperMan();
s.takeOff();
s.fly();
s.land();
Bird b = new Bird();
b.takeOff();
b.fly();
b.land();
AirPlane a = new AirPlane();
a.takeOff();
a.fly();
a.land();
– Flyer 클래스 (추상 클래스)를 상속받은 서브클래스 SuperMan, Bird, AirPlane의 인스턴스를 생성함.
• 다형성 활용하기
– SuperMan, Bird, AirPlane → Flyer타입으로 업캐스팅하여 다형성 활용.
→ Flyer타입의 참조변수 (레퍼런스) 하나로 여러 개의 인스턴스를 모두 참조할 수 있음.
Flyer f;
f = s; // Flyer f = new SuperMan();
– 슈퍼클래스의 참조변수를 먼저 선언하고,
부품을 갈아 끼워 넣듯이 서브클래스의 주소값 (인스턴스 & 참조변수)을 대입함.
→ 이미 위에서 SuperMan의 인스턴스를 생성했으므로 주소값을 가지고 있는 참조변수를 대입함.
f.takeOff();
f.fly();
f.land();
– SuperMan 클래스에서 오버라이딩 한 메서드들이 호출됨.
✓ 실행 결과
SuperMan 이륙
SuperMan 비행
SuperMan 착륙
f = b;
f.takeOff();
f.fly();
f.land();
– 이번에는 슈퍼클래스의 참조변수에 Bird 클래스의 주소값 (인스턴스 & 참조변수)을 대입함.
– Bird 클래스에서 오버라이딩 한 메서드들이 호출됨.
✓ 실행 결과
Bird 이륙
Bird 비행
Bird 착륙
f = a;
f.takeOff();
f.fly();
f.land();
– 같은 방식으로 슈퍼클래스의 참조변수에 AirPlane 클래스의 주소값 (인스턴스 & 참조변수)을 대입함.
– AirPlane 클래스에서 오버라이딩 한 메서드들이 호출됨.
✓ 실행 결과
AirPlane 이륙
AirPlane 비행
AirPlane 착륙
EX3 ) 서브클래스에서 추상메서드의 오버라이딩 분할하기 |
• 슈퍼클래스 정의
abstract class AbstractClass2{
// 추상메서드 정의
public abstract void method1();
public abstract void method2();
}
• 서브클래스 MiddleClass 정의
abstract class MiddleClass extends AbstractClass2{
@Override
public void method1() {
System.out.println("MiddleClass에서 구현한 method1()");
}
}
– 추상메서드를 포함하는 추상클래스를 상속받는 서브클래스에서는 반드시 오버라이딩 해야 함.
– 상속받은 추상메서드 2개 중 하나만 구현한다면,
남은 하나의 추상메서드가 구현되지 않은 채 그대로 존재하므로 해당 서브클래스도 추상클래스로 선언해야 함.
• 서브클래스 SubClass2 정의
– 추상메서드를 모두 구현하지 않은 서브클래스 (추상클래스) MiddleClass를 상속받음.
class SubClass2 extends MiddleClass{
@Override
public void method2() {
System.out.println("SubClass2에서 구현한 method2()");
}
}
– method1() 메서드는 MiddleClass에서 이미 구현되어 있으므로 오버라이딩 강제성이 없음.
– 여전히 강제성이 남아있는 method2() 메서드는 반드시 구현해야 함.
→ 할아버지가 못 이룬 꿈 아버지가 이루려고 했는데 다 못 이뤄서, 남은 꿈 내가 마저 이룸 ! ^.<—★
[ Test ] |
1. 전기차(ElectricCar)와 디젤차(DiselCar)의 공통적인 요소를 추상화하여 차량(Vehicle) 클래스에 정의.
2. 공통요소
– curX, curY : 현재 위치의 좌표를 저장하는 변수.
– reportPosition() : 연료공급지의 위치 출력.
– 출력형태 : "현재 위치" : curX, curY"
– addFuel() : 연료 공급 방법 출력.
ex ) 전기차 충전소에서 배터리 충전, 주요소에서 디젤 연료 공급.
• 슈퍼클래스 Vehicle 정의
abstract class Vehicle{
int curX, curY;
public void reportPosition(int curX, int curY) {
System.out.println("현재 위치 : " + curX + ", " + curY);
}
public abstract void addFuel();
}
– 차량마다 연료 공급 방법이 달라지므로 각 차량 클래스마다 오버라이딩 해야 함.
→ '무조건 오버라이딩' 해야 하므로 강제성 부여를 위해 추상메서드로 정의함.
– 해당 클래스는 추상메서드를 가지게 되었으므로 추상클래스가 되야 함.
• 서브클래스 ElectricCar 정의
class ElectricCar extends Vehicle{
@Override
public void addFuel() {
System.out.println("전기차 충전소에서 배터리 충전");
}
}
• 서브클래스 DiselCar 정의
class DiselCar extends Vehicle{
int hi;
@Override
public void addFuel() {
System.out.println("주유소에서 디젤 연료 공급");
}
}
• 업캐스팅으로 다형성 활용하기
Vehicle vc = new ElectricCar();
vc.addFuel();
vc.reportPosition(3, 7);
vc = new DiselCar();
vc.addFuel();
vc.reportPosition(10, 7);
//vc.hi
// 접근 불가
// 접근 가능
DiselCar d = (DiselCar) vc;
d.addFuel();
d.hi = 0;
– 슈퍼클래스가 추상클래스이므로 인스턴스 생성 불가.
→ 서브클래스의 인스턴스를 생성하여 슈퍼클래스 타입으로 업캐스팅 후, 슈퍼클래스의 멤버에 접근하기.
→ 어떤 서브클래스의 인스턴스가 오냐에 따라 해당 서브클래스에서 오버라이딩 된 메서드가 호출됨.
– 업캐스팅으로 인해 서브클래스의 멤버에 접근 불가.
→ 다운캐스팅으로 DiselCar의 멤버에 접근 가능.
✓ 실행 결과
전기차 충전소에서 배터리 충전
현재 위치 : 3, 7
주유소에서 디젤 연료 공급
현재 위치 : 10, 7
주유소에서 디젤 연료 공급