다형성(polymorphism)
다형성이란
객체지향에서 다형성이란 '여러 가지 형태를 가질 수 있는 능력'을 의미
자바에서는 한 타입의 참조 변수로 여러 타입의 객체를 참조할 수 있도록 함
즉, 자식 클래스의 인스턴스를 부모 타입의 참조변수로 참조하는 것이 가능
Class Tv {
boolean poser; //전원상태(on/off)
int channel; //채널
void power() {
power = !power;
}
void channelUp() {
power = !power;
}
void channelDown() {
power = !power;
}
}
Class CaptionTV extends Tv {
String text; //캡션을 보여주기 위한 문자열
void caption() {
//...내용생략
}
}
Tv와 CaptionTv는 서로 상속관계
이 두 클래스의 인스턴스를 생성하고 사용하려면 다음과 같이 작성해야 한다
Tv t = new Tv();
CaptionTv c = new CaptionTv();
하지만 두 클래스가 서로 상속 관계에 있기 때문에 참조변수를 각각 Tv 와 CaptionTv로 다르게 사용할 필요 없이
Tv t = new CaptionTv(); //부모 타입의 참조변수로 자식 인스턴스를 참조
이런식으로 사용 할 수 있다
그럼 같은 타입으로 참조하는 것과 부모 타입으로 참조하는 것에는 어떤 차이가 있을까?
CaptionTv c = new CaptionTv();
Tv t = new CaptionTv();
여기서 t와 같이 인스턴스의 타입과 참조 변수의 타입이 다를 경우 CaptionTv의 모든 멤버를 사용할 수 없다
t는 CaptionTv의 인스턴스 중 Tv 클래스의 멤버만 사용할 수 있다
즉, Tv 클래스에 정의되지 않은 text와 caption()은 사용이 불가능하다
둘다 같은 CaptionTv를 참조하는 인스턴스지만
참조변수(CaptionTv / Tv)의 타입에 따라 사용할 수 있는 멤버의 개수가 달라진다
반대로 자식 타입으로 부모 타입의 인스턴스를 참조하는 것은 가능할까?
불가능 하다
CaptionTv c = new Tv(); //컴파일 에러 발생
실제 인스턴스인 Tv의 멤버 개수보다 참조변수 c가 사용할 수 있는 멤버 개수가 더 많기 때문
그래서 이를 허용하지 않는다
참조변수가 사용할 수 있는 멤버의 개수는 인스턴스의 멤버 개수보다 같거나 적어야 한다
⭐클래스는 상속을 통해 확장될 수는 있어도 축소될 수는 없어서, 부모 인스턴스의 멤버 개수는 자식보다 항상 적거나 같다 ⭐
참조변수의 형변환
참조변수도 기본 변수와 같이 형변환이 가능하다
단, 서로 상속관계에 있는 클래스 사이에서만 가능하기 때문에 자손 -> 조상, 조상 -> 자손만 가능하다
자식 타입 -> 부타입 (Up-casting) : 형변환 생략가능
자식타임 <- 부모타입 (Downd-casting) : 형변환 생략불가
작은 자료형에서 큰 자료형의 형변환은 생갹이 가능하듯이 참조 변수도 부모 타입으로 형변환하는 경우 생략이 가능하다
참조변수간 형변환 역시 캐스트 연산자를 사용하며, 괄호()안에 변환하고자 하는 타입의 이름을 적어주면 됨
class Car {
String color;
int door;
void drive() {
System.out.println("drive, Brrrr~");
}
}
instanceof 연산자
참조변수와 인스턴스의 연결
매개변수의 다형성
여러 종류의 객체를 배열로 다루기
추상클래스(abstract class)
추상 클래스란?
- 새로운 클래스를 작성하는데 있어서 바탕이 되는 부모 클래스
추상클래스는 자식 클래스의 상속에 의해서만 완성될 수 있다
추상클래스 자체로는 클래스의 역할을 할 수 없으며, 인스턴스 또한 생성할 수 없다
새로운 클래스를 만들 때 아무것도 없는 상태에서 시작하는 것 보다는 어느정도 틀을 갖춘 상태에서 시작하는 것이 낫다
=> 그럴 때 사용하는 게 추상 클래스!
키워드 abstract를 붙이기만 하면 됨
이 클래스를 사용할 때 선언부의 abstract를 보고 이 클래스에는 추상메서드가 있으니 상속을 통해서 구현해야 한다는 것을 쉽게 알 수 있다
abstract class 클래스이름 {
...
}
추상 클래스는 일반 클래스와 전혀 다르지 않다
생성자, 메서드, 멤버변수 모두 가질 수 있다
추상메서드
추상메서드는 선언부만 작성하고 구현부는 작성하지 않은 채로 남겨둔다
구현부를 작성하지 않는 이유는 메서드의 내용이 상속받는 클래스에 따라 달라질 수 있기 때문
그래서 부모 클래스에서는 선언부만 작성하고 주석으로 어떤 기능을 수행하는지 적어주고
실제 내용은 상속받는 자식 클래스에서 구현하도록 비워둔다
따라서 추상클래스를 상속받는 자식 클래스는 부모의 추상메서드를 상황에 맞게 적절히 구현해주어야 한다
추상 메서드 역시 abstract를 붙이고 구현부가 없기 때문에 {} 대신 ;을 붙인다
/*주석을 통해 어떤 기능을 수행할 목적으로 작성하였는지 설명한다*/
abstract 리턴타입 메서드 이름();
추상클래스로부터 상속받는 자식 클래스는 오버라이딩을 통해 부모인 추상클래스의 추상 메서드를 모두 구현해주어야 한다
만일 부모로부터 상속받은 추상메서드 중 하나라도 구현하지 않는다면, 자식 클래스 역시 추상클래스로 지정해주어야 한다
abstract class Player { //추상 클래스
//추상 메서드
abstract void play(int pos);
abstract void stop();
}
class AudioPlayer extends Player {
void play(int pos){
//추상메서드 구현
}
void stop(){
//추상메서드 구현
}
}
abstract class AbstractPlayer extends Player {
void play(int pos) {
//추상메서드 구현...
}
}
구현부가 없는 메서드라 의미가 없어 보여도 매우 중요한 부분이 선언부이다
메서드를 사용하는 입장에서는 메서드가 어떻게 구현되어있는지 몰라도
메서드의 이름과 매개변수, 리턴타입을 알고 있으면 되기 때문이다
추상클래스의 작성
여러 클래스에 공통적으로 사용될 수 있는 클래스를 바로 작성하기도 하고
기존의 클래스의 공통적인 부분을 뽑아서 추상클래스로 만들어 상속하도록 하는 경우도 있다
다음은 Player라는 추상 클래스이다
이 클래스는 VCR이나 Audio와 같은 재생 가능한 기기를 클래스로 작성할 때, 이 둘의 부모 클래스로 사용가능하다
abstract class Player{
boolean pause;
int currentPos;
Player(){
pause = false;
currentPos = 0;
}
abstract void play(int pos);
abstract void stop();
void play(){
play(currentPos);
}
void pause(){
if(pause){
pause = false;
play(currentPos);
}
else{
pause = true;
stop();
}
}
}
이제 Player 클래스를 조상으로 하는 CD Player 클래스를 만들어보자
class CDPlayer extends Player {
void play(int currentPos) {
// 부모의 추상 메서드 구현
}
void stop() {
// 부모의 추상 메서드 구현
}
void nextTrack() {
currentTractk++;
}
void preTrack() {
currentTractk--;
}
}
부모 클래스의 추상메서드를 CDPlayer 클래스의 기능에 맞게 완성해준 다음 CDPlayer 만의 새로운 기능들을 추가해주었다
'Language > Java' 카테고리의 다른 글
[Java] 내부 클래스(Inner Class) (0) | 2024.04.18 |
---|---|
[Java] 인터페이스 (0) | 2024.04.18 |
[Java] 객체지향 프로그래밍 - 제어자 (0) | 2024.04.05 |
[Java] 객체지향 프로그래밍 - package와 import (0) | 2024.04.05 |
[Java] 객체지향 프로그래밍 - 오버라이딩(overriding) (0) | 2024.04.05 |