부모 클래스 타입의 참조 변수로 자식 클래스 타입의 인스턴스를 참조할 수 있다
단 부모 클래스가 자식 클래스를 참조할 때 부모 자신의 멤버와 오버라이딩 메서드만 접근할 수 있다
class Parent { ... }
class Child extends Parent { ... }
Parent pa = new Parent(); // 허용
Child ch = new Child(); // 허용
Parent pc = new Child(); // 허용
Child cp = new Parent(); // 에러
클래스는 상속을 통해 확장될 수는 있어도 축소될 수는 없으므로, 자식 클래스에서 사용할 수 있는 멤버의 개수가 언제나 부모 클래스와 같거나 많게 된다
때문에 자식 클래스 타입의 참조 변수로 부모 클래스 타입을 참조할 수 없게 된다
class Phone {
void brand() {
System.out.println("Galaxy");
}
}
class Note9 extends Phone {
void merit() {
System.out.println("Classic, Wide");
}
}
class S2 extends Phone {}
Phone phone = null;
Note9 note = new Note9();
Note9 note2 = null;
S2 s = null;
note.merit();
phone = (Phone) note; // 자식 타입을 부모 타입으로 형변환
// phone.merit(); // 에러! 부모 타입은 자식의 멤버를 호출할 수 없음 (오버라이딩 제외)
note2 = (Note9) phone; // 부모 타입을 자식 타입으로 형변환
note2.merit();
// s = (Note9)note; // 상속 관계가 아닌 참조변수는 형변환 불가
참조변수의 형변환은 사용할 수 있는 멤버의 개수를 조절하는 방법이다
부모 자식 관계의 참조변수는 서로 형변환이 가능하지만 상속 관계가 아닌 참조 타입끼리는 형변환을 할 수 없다
System.out.println(phone instanceof Phone); // true
System.out.println(phone instanceof Note9); // false
// 자식이 조상으로 형변환 가능여부
System.out.println(note instanceof Object); // true
System.out.println(note instanceof Phone); // true
System.out.println(note instanceof Note9); // true
참조변수의 형변환 가능여부를 확인할 때 사용하며 boolean 값을 반환한다
작업 중 형변환을 할 때 전에 반드시 instanceof
로 확인하는 과정을 거쳐야 한다
// 주문 앱 시나리오
class Food {
int price;
int bonuspoint;
Food() {}
Food(int price) {
this.price = price;
this.bonuspoint = (int) (price / 10.0);
}
}
class Chicken extends Food {
Chicken() { super(20000); } // Food(int price) 생성자를 호출
public String toString() { return "Chicken"; }
}
class Pizza extends Food {
Pizza() { super(15000); }
public String toString() { return "Pizza"; }
}
class Order {
int money;
int bonuspoint;
/*
void buy(Chicken chicken) {}
void buy(Pizza pizza) {}
*/
// 부모 타입인 Food 타입으로 자식 타입인 Chicken, Pizza를 받아올 수 있음
void buy(Food food) {
this.money -= food.price; // 가진 돈에서 음식의 가격을 뺀다
this.bonuspoint += food.bonuspoint; // 음식 가격만큼의 보너스를 추가한다
System.out.println("구매한 음식 : " + food.toString());
System.out.println("잔액 : " + this.money);
System.out.println("포인트 현황 : " + this.bonuspoint);
}
참조형 매개변수는 메서드 호출 시, 자신과 같은 타입 또는 자식타입의 인스턴스를 넘길 수 있다 상속관계에 있는 매개변수를 사용할 때 반복되는 코드를 줄이는 데 효과적이다
// 부모타입의 배열, 자식타입의 요소
Food f[] = new Food[2];
f[0] = new Chicken();
f[1] = new Pizza();
부모타입의 배열에 자식들의 객체를 담을 수 있다
abstract class Player { // 추상 클래스
// 인스턴스 변수
boolean pause;
int currentPos;
// 추상 클래스도 생성자가 있어야 한다
Player() {
pause = false;
currentPos = 0;
}
// 추상 메서드 (상속을 통해 완성시키도록)
abstract void play(int pos);
abstract void stop();
// 인스턴스 메서드
void play() {
play(currentPos); // 추상 메서드를 호출할 수 있다
}
}
class AudioPlayer extends Player { // 상속을 통해 구현
// 추상 메서드를 구현
void play(int pos) {}
void stop() {}
}
// 추상 메서드를 모두 구현하지 않았으므로 추상 클래스
abstract class AbstractPlayer extends Player {
void play(int pos) {}
// abstract void stop()이 생략되어 있음
}
Player p = new Player(); // 에러! 추상 클래스의 인스턴스 생성 불가
Audioplayer ap = new AudioPlayer();
Player ap2 = new AudioPlayer(); // 다형성의 특징인 부모 타입의 자식 인스턴스 참조 이용
abstract
키워드를 사용한다{}
가 없는 메서드추상 메소드가 포함된 클래스를 상속받는 자식 클래스가 반드시 추상 메소드를 구현하도록 하기 위함이다
만약 일반 메소드로 구현한다면 사용자에 따라 해당 메소드를 구현할 수도 있고, 안 할 수도 있다
하지만 추상 메소드가 포함된 추상 클래스를 상속받은 모든 자식 클래스는 추상 메소드를 구현해야만 인스턴스를 생성할 수 있으므로, 반드시 구현하게 됩니다
abstract class Unit {
// 공통으로 사용되는 부분
int x, y;
abstract void move(int x, int y); // 추상 메서드
void stop() {} // 현재 위치에 정지
}
class Marine extends Unit {
void move(int x, int y) {} // 추상 메서드 구현
void stimPack() {} // 스팀팩 사용
}
class Tank extends Unit {
void move(int x, int y) {}
void changeMode() {} // 공격모드 변환
}
class Dropship extends Unit {
void move(int x, int y) {}
void load() {} // 선택된 대상을 태운다
void unload() {} // 선택된 대상을 내린다
}
여러 클래스에 공통적으로 사용될 수 있는 추상 클래스를 바로 작성하거나 기존 클래스의 공통 부분을 뽑아서 추상 클래스를 만든다
// 1.
GregorianCalendar cal = new GregorianCalendar(); // 구체적
// 2.
Calendar cal = Calendar.getInstance(); // 추상적
1번의 코드는 구체적으로 어떤 클래스를 사용할 지 어떤 타입인 지 구체적으로 명시되어 있다
GregorianCalendar
는 서양력을 따르는 달력으로 다른 달력을 사용하려면 새로 정의해야 한다
2번의 코드의 Calendar
는 추상 클래스로 자손 객체를 반환하며 getInstance()
가 뭘 반환할 지 모호하게 작성되어 있어 태국력이나 서양력, 그외의 달력으로 새로 정의할 필요없이 반환받을 수 있다