JAVA

추상메서드와 추상클래스 (Abstract)

태로미 2023. 3. 29. 12:35

 

목차

1.   추상 메서드


2.   추상 클래스


EX1 )   기본 정의 및 사용

EX2 )   다형성 활용하기

EX3 )   서브클래스에서 추상메서드의 오버라이딩 분할하기


[ Test ]

 

 

 

 

 

 

 

 

 

 

📍   추상메서드 (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
주유소에서 디젤 연료 공급