1. abstract 키워드
- 클래스와 메서드에 적용
- 추상 클래스는 실제 클래스들의 멤버변수와 메서드들의 이름을 통일할 목적으로 사용
- 추상 메서드가 있는 클래스는 반드시 추상 클래스여야 함
- 추상클래스에 반드시 추상 메서드만 선언할 필요는 없고 일반 메서드도 선언할 수 있음
- abstract 키워드를 사용하지 않았을 때 문제가 되는 예시
public class PopupStore {
public void orderApple() {
System.out.println("착즙 사과주스를 내줍니다.");
}
public void orderOrange() {
System.out.println("착즙 오렌지주스를 내줍니다.");
}
public void orderGrape() {
System.out.println("착즙 포도주스를 내줍니다.");
}
}
public class Store extends PopupStore{
@Override
public void orderApple() {
System.out.println("착즙 사과주스를 팝니다. 가격은 20000원");
}
@Override
public void orderOrange() {
System.out.println("착즙 오렌지주스를 팝니다. 가격은 24000원");
}
// 실수로 포도주스 가격 업데이트를 누락
}
public class MainClass {
public static void main(String[] args) {
// 2가지 문제점 체크
// 1. 정식매장이 존재하는데 팝업스토어 생성 가능
PopupStore ps = new PopupStore();
// 2. 팝업스토어 클래스 내부에서 오버라이딩이 필수인 메서드가 누락될 수도 있음
PopupStore s = new Store();
s.orderApple();
s.orderOrange();
s.orderGrape();
}
}
위 예시와 같이 abstract 키워드를 이용하지 않았을 때 위의 2가지 문제점이 발생할 수 있다.
포도주스를 오버라이딩 하지 않으면 경고를 띄우게 해야 하지 않을까? → 추상화 적용
PopupStore(부모타입)으로 Store(자식) 객체 형변환이 이루어짐
부모타입으로 선언한 객체에서도 자식이 오버라이딩했던 함수의 내용이 출력되는 것을 확인할 수 있다. (Promotion-자동형변환)
1) abstract class
- new 키워드를 이용하여 객체 생성 불가
- 오직 상속을 통해서 자식클래스로 구체화 시켜야 함
- new를 사용하여 직접 생성자를 호출할 수는 없지만 자식 객체가 생성될 때 super()를 호출하여 추상 클래스 객체를 생성하므로 추상 클래스도 생성자가 반드시 있어야 함
- 추상 메서드를 하나 이상 보유하고 있다면 해당 클래스는 추상 클래스 → 무조건 abstract 키워드를 붙여줘야 함
2) abstract method
- 추상 클래스 내에서만 선언 가능
- 추상 메서드는 메서드의 선언부만 있고, 메서드 실행 내용이 들어가는 중괄호 {}가 없는 메서드를 말함
- 추상 클래스 설계 시 자식 클래스가 반드시 실행 내용을 채우도록 강요하고 싶은 메서드가 있을 경우 해당 메서드를 추상 메서드로 선언
- 자식 클래스에서 반드시 부모 추상 클래스의 추상 메서드를 재정의하여 실행 내용을 작성해야 함
3) abstract 사용 예제
public abstract class PopupStore {
// 1. 메서드에 abstract를 붙이면 해당 메서드는 추상메서드가 되고, 이 메서드는 반드시 오버라이딩 해야 함
// 2. 추상메서드는 상속을 목적으로 선언한 메서드
// 실행을 목적으로 선언된 메서드가 아니므로 메서드의 몸체({}) 부분이 없고 선언 마무리도 ;로 함
// 3. 일반 클래스에는 추상 메서드 선언 불가
// 추상메서드가 하나 이상 존재하면 무조건 추상클래스로 선언
// 4. 추상클래스 내부에서는 추상메서드가 하나 이상 존재한다면 일반메서드 선언도 여전히 가능
public abstract void orderApple();
public abstract void orderOrange();
public abstract void orderGrape();
public void refund() {
System.out.println("제품에 문제가 있어서 환불합니다.");
}
}
public class Store extends PopupStore {
@Override
public void orderApple() {
System.out.println("착즙 사과주스를 20000원에 팝니다.");
}
@Override
public void orderOrange() {
System.out.println("착즙 오렌지주스 24000원에 팝니다.");
}
@Override
public void orderGrape() {
System.out.println("가격은 못 정했습니다.");
}
}
public class ConvenientStore extends PopupStore {
@Override
public void orderApple() {
System.out.println("가당 사과주스 4000에 팝니다.");
}
@Override
public void orderOrange() {
System.out.println("가당 오렌지주스 5000에 팝니다.");
}
@Override
public void orderGrape() {
System.out.println("가당 포도주스 3500에 팝니다.");
}
}
public class MainClass {
public static void main(String[] args) {
// PopupStore 클래스는 직접 객체 생성 불가능
// PopupStore ps = new PopupStore();
// PopupStore s = new Store();
PopupStore s = new ConvenientStore();
// 객체 종류에 따라 실행구문이 다르게 정의되었지만 명세는 같은 메서드
s.orderApple();
s.orderOrange();
s.orderGrape();
// 어떤 객체가 와도 공통적으로 실행되는 메서드
s.refund();
}
}
※ 템플릿 메서드 패턴 활용
public abstract class Lottery {
// 템플릿 메서드 패턴은 큰 틀에서 호출구문은 구현메서드(실행문이 있는 메서드)로 정의해놓고
// 구현메서드가 호출하는 추상메서드들은 상속 후에 특징을 정하도록 만들어서
// 호출 순서는 그대로 가져갈 수 있도록 하되, 사용자가 특징만 정의하도록 하는 디자인 패턴
// 구현메서드는 큰 틀은 같지만, 세부사항이 달라질 수 있는 내용을 먼저 작성
public void lotteryCycle() {
// 1. 어디서 사는가?
buyLottery();
// 2. 당첨 여부 확인
checkWinLottery();
// 3. 당첨 시 수령
getLotteryMoney();
}
// 세부사항은 상속받은 주체가 무엇인지에 따라 다르게 정의할 수 있도록
// 추상메서드만 정의해놓고, 추가적인 작업은 하지 않음
abstract void buyLottery();
abstract void checkWinLottery();
abstract void getLotteryMoney();
}
public class KoreanLotto extends Lottery {
@Override
public void buyLottery() {
System.out.println("한 게임에 천원짜리 로또를 삽니다.");
}
@Override
public void checkWinLottery() {
System.out.println("45C6의 확률을 뚫고 1등에 당첨되었습니다.");
}
@Override
public void getLotteryMoney() {
System.out.println("1등 상금으로 대략 수십억을 받았습니다.");
}
}
public class StatesSuperball extends Lottery {
@Override
public void buyLottery() {
System.out.println("미국 가서 슈퍼볼 복권을 삽니다.");
}
@Override
public void checkWinLottery() {
System.out.println("69C5 * 26C1 분의 1의 확률로 당첨되었습니다.");
}
@Override
public void getLotteryMoney() {
System.out.println("당첨 금액은 최소 수천억원입니다.");
}
}
public class MainClass {
public static void main(String[] args) {
//Lottery lottery = new KoreanLotto();
Lottery lottery = new StatesSuperball();
lottery.lotteryCycle();
}
}