1. 다형성(Polymorphism)
1) 다형성이란?
- 객체지향개념의 중요한 특징 중 하나
- 객체지향 개념에서의 '다형성' : 여러 가지 형태를 가질 수 있는 능력
- 자바에서의 '다형성' : 한 타입의 참조변수로 여러 타입의 객체를 참조할 수 있도록 하는 것
- 구체적으로, 조상클래스 타입의 참조변수로 자손클래스의 인스턴스를 참조할 수 있도록 하는 것
class Tv {
boolean power;
int channel;
void power() { power = !power; }
void channelUp() { ++channel; }
void channelDown() {--channel; }
}
class CaptionTv extends Tv {
String text;
void caption() { // caption을 출력하는 내용 }
}
class Main {
public static void main(String[] args) {
// (1)
Tv t = new Tv();
CaptionTv c = new CaptionTv();
// (2)
Tv t2 = new CaptionTv();
}
}
(1) 참조변수 타입 = 인스턴스 타입
(2) 참조변수 타입은 조상 클래스(Tv) , 인스턴스 타입은 자식 클래스(CaptionTv)
-> 꼭 타입이 같지 않아도 됨
(1) CaptionTv c = new CaptionTv();
(2) Tv t = new CaptionTv();
(1) c는 CaptionTv인스턴스의 모든 멤버를 사용할 수 있음
(2) t는 CaptionTv인스턴스의 모든 멤버를 사용할 수 없음, Tv클래스의 멤버들(상속받은 멤버 포함)만 사용 가능
-> 둘 다 같은 타입의 인스턴스지만 참조변수의 타입에 따라 사용할 수 있는 멤버의 개수가 달라짐
CaptionTv c = new Tv(); // 컴파일 에러!!
-> 자손타입의 참조변수로 조상타입의 인스턴스를 참조하는 것은 불가능함
-> 조상타입에는 존재하지 않는 멤버를 사용하고자 할 가능성이 있기 때문
-> 참조변수가 사용할 수 있는 멤버의 개수는 인스턴스의 멤버 개수보다 같거나 적어야 함
1. 조상타입의 참조변수로 자손타입의 인스턴스를 참조 (O)
2. 자손타입의 참조변수로 조상타입의 인스턴스를 참조 (X)
2) 참조변수의 형변환
자손타입 -> 조상타입 (Up-casting) : 형변환 생략가능
자손타입 <- 조상타입 (Down-casting) : 형변환 생략불가
업캐스팅(Up-casting) <-> 다운캐스팅(Down-casting)
class Car {
...
}
class FireEngine extends Car {
...
}
class Ambulance extends Car {
...
}
class Main {
public static void main(String[] args) {
// (1) 상속관계가 아닌 클래스 간의 형변환 불가능
FireEngine f;
Ambulance a;
a = (Ambulance)f; // 에러
f = (FireEngine)a; // 에러
// (2) 업캐스팅
Car c = null;
FireEngine fe = new FireEngine();
c = fe; // 형변환 생략, 업캐스팅
// (3) 다운캐스팅\
Ambulance am = null;
am = (Ambulance)c; // 형변환 필수, 다운캐스팅
}
}
- 참조변수간의 형변환 역시 캐스트연산자()를 사용함
- 형변환은 참조변수의 타입을 변환하는 것 (O), 인스턴스를 변환하는 것 (X) 이므로 인스턴스에 아무런 영향을 미치지 않음
- 참조하고 있는 인스턴스에서 사용할 수 있는 멤버의 범위(개수)를 조절하는 것뿐임
※ 참조변수가 가리키는 인스턴스의 자손타입으로 형변환은 허용되지 않음
...
Car car = new Car();
FireEngine fe = null;
fe = (FireEngine)car; // 실행 시 에러 발생!
...
3) instanceof 연산자
- 참조변수가 참조하고 있는 인스턴스의 실제 타입을 알아보기 위해 사용
- 주로 조건문에 사용
- 연산의 결과로 true/false 반환
true : 참조변수가 검사한 타입으로 형변환이 가능함
false : 참조변수가 검사한 타입으로 형변환이 불가능함 또는 참조변수의 값이 null임
...
if (c instanceof FireEngine) {
...
}
else if (c instanceof Ambulance) {
...
}
...
4) 참조변수와 인스턴스의 연결
- 조상 클래스에 선언된 멤버변수와 같은 이름의 인스턴스변수를 자손 클래스에 중복으로 정의했을 때, 인스턴스의 타입에 따라 서로 다른 결과를 얻음
- 메서드 : 항상 실제 인스턴스의 메서드(오버라이딩된 메서드)가 호출
- 멤버변수 : 참조변수의 타입에 따라 달라짐
(1) 조상타입의 참조변수 사용 : 조상 클래스에 선언된 멤버변수 사용
(2) 자손타입의 참조변수 사용 : 자손 클래스에 선언된 멤버변수 사용
class Parent {
int x = 100;
void method() { System.out.println("Parent Method");
}
class Child extends Parent {
int x = 200;
void method() { System.out.println("Child Method");
}
class Main {
public static void main(String[] args) {
Parent p = new Child();
Child c = new Child();
System.out.println(p.x);
p.method();
System.out.println(c.x);
c.method();
/* 출력결과
100
Child Method
200
Child Method
*/
}
5) 매개변수의 다형성
- 참조변수의 다형적인 특징은 메서드의 매개변수에도 적용됨
class Product {
int price;
}
class Tv extends Product { }
class Computer extends Product { }
class Buyer {
int money = 200;
// void buy(Tv t) { money -= t.price; }
// void buy(Computer c) { money -= c.price; }
// 조상 클래스인 Product 타입을 매개변수로 하여 간단히 처리 가능
void buy(Product p) { money -= p.price; }
}
6) 여러 종류의 객체를 배열로 다루기
- 조상타입의 참조변수 배열을 사용하면, 공통의 조상을 가진 서로 다른 종류의 객체를 배열로 묶어서 다룰 수 있음
class Buyer {
int money = 1000;
Product[] item = new Product[10];
int i = 0;
void buy(Product p) {
itme[i++] = p;
}
}
- 배열 크기가 가변적인 것은 Vector 클래스를 사용함
- Vector 클래스 : 동적으로 크기가 관리되는 객체배열
메서드 / 생성자 | 설 명 |
Vector() | 10개의 객체를 저장할 수 있는 Vector인스턴스를 생성 10개 이상의 인스턴스가 저장되면, 자동으로 크기 증가됨 |
boolean add(Object o) | Vector에 객체를 추가함 추가에 성공하면 true를, 실패하면 false를 반환함 |
boolean remove(Object o) | Vector의 객체를 제거함 제거에 성공하면 true를, 실패하면 false를 반환함 |
boolean isEmpty() | Vector가 비어있으면 true를, 비어있지 않으면 false를 반환함 |
Object get(int index) | 지정된 위치(index)의 객체를 반환 반환타입이 Object타입이므로 형변환 필요 |
int size() | Vector에 저장된 객체의 개수 반환 |
2. 추상클래스(Abstract class)
1) 추상클래스란?
- 미완성된 클래스, 즉 미완성 메서드(추상메서드)를 포함하고 있는 클래스
- 인스턴스를 생성할 수 없음
- 상속을 통해 자손클래스에 의해서만 완성될 수 있음
Q. 추상클래스는 왜 사용하는가?
A. 새로운 클래스를 작성하는데 있어서 바탕이 되는 조상클래스로서 중요한 의미를 가짐
abstract class 클래스명 {
...
}
2) 추상메서드(abstract method)
- 선언부만 작성하고 구현부는 작성하지 않은 채로 남겨 둔 메서드
Q. 추상메서드는 왜 구현부를 작성하지 않는가?
A. 메서드의 내용이 상속받는 클래스에 따라 달라질 수 있기 때문에 실제 내용(구현부)은 상속받는 클래스에서 구현하도록 비워두는 것임
/* 주석을 통해 어떤 기능을 수행할 목적으로 작성하였는지 설명함 */
abstract 리턴타입 메서드이름();
- 만약 조상의 추상메서드 중 하나라도 구현하지 않는다면, 자손클래스 역시 추상클래스로 지정해줘야 함
abstract class Player { // 추상클래스
abstract void play(int pos); // 추상메서드1
abstract void stop(); // 추상메서드2
}
class Player1 extends Player {
void play(int pos) { ... } // 추상메서드1 구현
void stop() { ... } // 추상메서드2 구현
}
abstract class Player2 extends Player { // 추상클래스
void play(int pos) { ... } // 추상메서드1 구현
// 추상메서드2 구현X -> 추상클래스가 됨
}
- 추상클래스는
(1) 여러 클래스에 공통적으로 사용될 수 있는 클래스를 바로 작성하기도 하고,
(2) 기존의 클래스의 공통적인 부분을 뽑아서 추상클래스로 만들기도 함
※ 추상화와 구체화의 차이
추상화 : 클래스간의 공통점을 찾아내서 공통의 조상을 만드는 작업
구체화 : 상속을 통해 클래스를 구현, 확장하는 작업
참고 - Java의 정석 3rd Edition (저자 : 남궁성, 출판 : 도우출판)
'공부 > Java' 카테고리의 다른 글
[Java-08] 예외처리(Exception Handling) (0) | 2021.03.04 |
---|---|
[Java-07] 객체지향프로그래밍Ⅱ - 인터페이스, 내부 클래스 (0) | 2021.03.03 |
[Java-07] 객체지향프로그래밍Ⅱ - 패키지, import문, 제어자 (0) | 2021.02.24 |
[Java-07] 객체지향프로그래밍Ⅱ - 상속, 오버라이딩 (0) | 2021.02.22 |
[Java-06] 객체지향프로그래밍 - 오버로딩, 생성자 (0) | 2021.02.18 |