[객체지향][다형성] 추상클래스

포키·2022년 11월 3일
0

국비과정

목록 보기
25/73

댄 페냐의 성공을 위한 마음가짐


추상 클래스 (abstract class)

정의

  • 추상메서드를 포함할 수 있는 클래스
  • class 앞에 abstract를 붙여 표현
  • 상속에서 추상적인 개념을 나타내기 위한 용도로 사용
  • leaf node가 될 수 없다 (언제나 자식 클래스를 상정하고 만들어진다.)

추상메서드
: body가 없는 메서드

public void toDo() {
}

public void toDo() : header <- WHAT to do
{ } : body <- HOW to do

  • 추상메서드란 body가 비워진 메서드
  • 정의(what)만 있고 내용(how)는 없다.
  • 하위 클래스가 해당 메서드를 반드시 오버라이드하도록 만든다.

예시

abstract class Shape {
	// 추상메서드를 포함하는 추상클래스
	public abstract int getArea();
    	// body가 비워진 추상메서드
}
class Circle extends Shape {
	@Override
	public int getArea() {
    	// 원의 넓이 구하여 return
    }
}
class Rectangle extends Shape {
	@Override
	public int getArea() {
    	// 사각형의 넓이 구하여 return
    }
}

클래스 다이어그램 표기법

  • abstract class -> <<abstract>> 표시
  • abstract method -> abstract 표시 (이탤릭체)

(일반)클래스 vs 추상클래스

  • 추상메서드가 없어도, 객체를 생성하지 않을 클래스는 추상클래스로 만들기도 한다.
  • 추상클래스는 하위개념을 위해 존재하는 것
  • 추상클래스를 내려받은 일반클래스가 부모의 추상메서드를 오버라이드하지 않으면 ERROR
    일반 클래스가 추상 메서드를 가질 수 없기 때문 (추상 -> 추상은 가능)

예제 - 추상클래스&추상메서드의 사용

abstract class Super {
	private int num;

	public Super(int num){
		setNum(num);
	}
	public void setNum(int num) {
		this.num = num;
	}
	public int getNum() {
		return num;
	}
	abstract public void todo();
	/*
		1. call() 호출
			>> Super는 객체생성 불가. Super의 하위객체 존재 유추.
		2. Super는 추상클래스. 추상메서드 todo() 가지고 있음.
			>> 하위클래스는 일반클래스이다. todo()가 오버라이드 되었다는 것이 보장된다.
		3. 상속관계에서 오버라이드를 한 경우 무조건 오버라이드된 메서드가 호출된다.
			>> call()에서 호출하는 todo()는 하위클래스의 오버라이드된 todo()이다.
	*/
	public void call() {
		todo();
	}
}
class Sub extends Super {
	public Sub(int num) {
		super(num);
	}
	@Override
	public void todo(){
	}
}
class Ex1 {
	public static void main(String[] args) {
		Sub s = new Sub(100);
		System.out.println(s.getNum());
	}
}
  • (추상클래스 안의) 추상메서드가 호출된다는 것은
    -> 추상클래스는 자체 객체생성 X
    -> 추상클래스를 상속받는 일반클래스가 객체를 만들었을 것이다
    -> 당연히 추상클래스 안의 모든 추상메서드도 오버라이드 되었을 것이다
  • 즉 추상메서드는 기본적으로 자식 객체에서 오버라이드 될 것을 상정하기 때문에, 클래스 내에서 추상 메서드를 일반 메서드처럼 호출해도 아무 문제가 없다.

예제 - abstract, final 키워드 사용

// 프렌차이즈
abstract class JavaBurger {
	// 오버라이드 금지, 내용 바꿀 수 없음
	public final void welcome() {
		greeting();
		order();
		serve();
		sayGoodBye();
	}
	protected abstract void greeting();
	private void order() {
		System.out.println("자바버거의 메뉴를 보여주고 주문을 받는다.");
	}
	private void serve() {
		System.out.println("주문한 자바버거를 서빙한다.");
	}
	private void sayGoodBye() {
		System.out.println("안녕히 가세요~ 자바버거에 또 오세요~");
	}
}
class JavaBurgerBusan extends JavaBurger {
	@Override
	protected void greeting() {
		System.out.println("어서오이소~ 자바버거입니더~");
	}
}
class JavaBurgerSeoul extends JavaBurger {
	@Override
	protected void greeting() {
		System.out.println("어서오세요~ 자바버거입니다~");
	}
}
class JavaBurgerJeju extends JavaBurger {
	@Override
	protected void greeting() {
		System.out.println("혼저옵서예~ 자바버거마씸~");
	}
}
class Ex2 {
	public static void main(String[] args) {
		JavaBurger j = new JavaBurgerSeoul();
		j.welcome();
	}
}

용도 (<- 한 번 더 확인하기)

  1. 상위개념의 표현 (구체적이지 않은, 추상적 개념 설명)
  2. 객체 생성하고 싶지 않은 클래스 (추상클래스는 꼭 추상메서드와 관계있는 건 아닐 수도 있다.)

상속은 하위 클래스에 룰을 정해주는 역할 (+ 중복 제거)
추상클래스/추상메서드는 자식 클래스가 지켜야 할 틀, 지켜야 할 룰을 정해준다.
(오버라이드를 강제함 = 메소드 이름, 패러미터, 리턴을 모두 상위 클래스에서 정함)
ex)

class 삼각형에서 넓이 구하는 메서드 : `int area(){ (삼각형 넓이 공식) }`
class 사각형에서 넓이 구하는 메서드 : `int RecArea(int width, int height){ (사각형 넓이 공식) }`

유사한 동작을 수행하지만 전부 방법이 다른 '넓이 구하기' 메서드

`class Shape`에서 `abstract int getArea();` 정의
	-> `class 삼각형`에서 오버라이드 `int gerArea(){ (삼각형 넓이 공식) }`
	-> `class 사각형`에서 오버라이드 `int gerArea(){ (사각형 넓이 공식) }`

상위클래스의 추상메서드에서 getArea()를 미리 정의하는 것으로, 하위 클래스들의 메서드 정의 또한 정해진다.

is-a / has-a / use-a

  • UML 화살표와 의미
  • has-a : association = aggregation + composition - 멤버변수로 등장
  • is-a : inheritance - 1. 상속 + 2. ???로 등장
  • use-a : dependency - 지역변수로 등장
class Restaurant {
	private Food food;
	private Customer customer;

	public Restaurant() {
		// composition(association = has-a)
		food = new Food();
	}
	public void setCustomer(Customer customer) {
		// aggregation(association = has-a)
		this.customer = customer;
	}
	// dependency
	public void pay(Bill bill) {
		customer.pay(bill);
	}
}
  • 우리가 일반적으로 사용하는 has-a는 association
  • aggregation과 composition을 나누는 기준은 이해하기 어렵고 사람마다 해석이 다를 수 있다
    굳이 구분하려 하지 말자
  • 일반적으로 더 중요한 관계는 is-a, has-a
  • dependency는 흔하게 쓰고 너무 많아서 자주 표시를 생략함

UML은 읽을 줄은 알아야 하지만, 아직 쓸 줄은 몰라도 된다.

profile
welcome

0개의 댓글