728x90
다형성(polymorphism)
- 여러가지 형태를 가질 수 있는 능력
- 하나의 참조변수로 여러 타입의 객테를 참조할 수 있는 것이다.
- 조상타입의 참조변수로 자손타입의 인스턴스를 참조할 수는 있지만, 자손타입의 참조변수로 조상타입의 인스턴스를 참조할 수 없다.
class Tv {
boolean power;
int channel;
void power() {power = !power;}
void channelUp() {++channel;}
void ChannelDown() {--channel;}
}
class Caption extends Tv {
String text;
void caption() {//글자를 보여준다.}
}
Tv t = new Tv();
CaptionTv c = new CaptionTv();
Tv t = new CaptionTv();
CaptionTv c = new Tv(); // 컴파일 에러 발생
참조변수의 형변환
- 서로 상관관계에 있는 타입간의 형변환만 가능하다.
- 자손 타입에서 조상 타입으로 형변환하는 경우는 생략이 가능하다.
- 자손타입 → 조상타입(up-casting) : 형변환 생략 가능
- 조상타입 → 자손타입(down-casting) : 형변환 생략 불가
instanceof 연산자
- 참조변수가 참조하는 인스턴스의 실제타입에 적용되는지 체크한다.
- 이항연산자이며 피연산자는 참조형변수와 타입
- 연산결과는 true, false
- 연산결과가 true이면, 해당타입으로 형변환이 가능하다.
참조변수와 인스턴스변수의 연결
- 멤버변수가 중복정의된 경우
참조변수의 타입에 따라 연결되는 멤버변수가 달라진다.
(참조변수타입에 영향을 받는다.)
- 메서드가 중복정의된 경우
참조변수의 타입에 관계없이 항상 실제 인스턴스의 타입에 정의된 메서드가 호출된다.
(참조변수타입에 영향을 받지 않는다.)
- 멤버변수가 조상클래스와 자손클래스에 중복으로 정의된 경우
조상타입의 참조변수를 사용했을 때는 조상클래스에 선언된 멤버변수가 사용된다.
자손타입의 참조변수를 사용했을 때는 자손클래스에 선언된 멤버변수가 사용된다.
매개변수의 다형성
- 참조형 매개변수는 메서드 호출시 자신과 같은 타입 또는 자손타입의 인스턴스를 넘겨줄 수 있다.
여러 종류의 객체를 하나의 배열로 사용하기
- 조상타입의 배열에 자손들의 객체를 담을 수 있다.
Product p1 = new Tv();
Product p2 = new Audio();
Product p3 = new NoteBook();
Product p[] = new Product[3];
p[0] = new Tv();
p[1] = new Audio();
p[2] = new NoteBook();
추상클래스
- 클래스를 설계도(~에 대한 정의)라고 한다면 추상클래스는 미완성설계도라고 할 수 있다.
- 추상메서드(미완성메서드)를 포함하고 있는 클래스다.(추상메서드 : 선언부만 있고 구현부가 없는 메소드)
abstract class Player {
int currentPos; // 현재 플레이어의 위치를 저장하기 위한 변수
Player() {currentPos = 0;}
abstract void play(int pos); // 추상메서드
abstract void stop(); // 추상메서드
void play() {
play(currentPos); // 추상메서드를 사용할 수 없다.
}
}
- 일반메서드는 추상메서드를 호출할 수 없다.
- 완성된 설계도가 아니므로 인스턴스를 생성할 수 없다.
- 다른 클래스를 작성하는데 도움을 줄 목적으로 작성된다.
추상메서드
- 선언부만 있고 구현부가 없는 메서드
- 꼭 필요하지만 자손마다 다르게 구현될 것으로 예상되는 경우에 사용
- 추상클래스를 상속받는 자손클래스에서 추상메서드의 구현부를 완성해야 한다.
abstract class Player {
abstract void play(int pos); // 추상메서드
abstract void stop(); // 추상메서드
}
class Mp3Player extends Player {
void play(int pos) {/* 내용을 작성 */}
void stop() {}
}
추상클래스 작성
- 여러 클래스에 공통적으로 사용될 수 있는 추상클래스를 바로 작성하거나 기존 클래스의 공통부분을 추출해서 추상클래스를 만든다.
class Marine {
int x, y;
void move(int x, int y) {/* 지정된 위치로 이동한다. */}
void stop() {/* 현재위치에 정지한다. */}
void stimPack() {/* 스팀팩을 맞는다. */}
}
class Tank {
int x, y;
void move(int x, int y) {/* 지정된 위치로 이동한다. */}
void stop() {/* 현재위치에 정지한다. */}
void siegeMode() {/* 시즈모드 활성화(비활성화) */}
}
class DropShip {
int x, y;
void move(int x, inty) { /* 지정된 위치로 이동한다. */ }
void stop() { /* 현재위치에 정지한다. */ }
void load() { /* 선택된 병력을 태운다. */ }
void unload() { /* 병력을 내린다. */ }
}
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 seigeMode() {/* 고유한 기능을 구현한다. */}
}
class DropShip extends Unit {
void move(int x, int y) {/* 내용을 구현한다. */}
void load() {/* 고유한 기능을 구현한다. */}
void unload() {/* 고유한 기능을 구현한다. */}
}
Unit[] group = new Unit[4];
group[0] = new Marine();
group[1] = new Tank();
group[2] = new DropShip();
group[3] = new Marine();
for(int i = 0; i < group.length; i++) {
// 실제로 구현된 move(int x, int y)가 호출된다.
group[i].move(100, 200);
}
추상
- 낱낱의 구체적 표상이나 개념에서 공통된 성질을 뽑아 이를 일반적인 개념으로 파악하는 정신 작용.
- 추상(抽象)은 사물을 정확하게 이해하기 위해서는 사물이 지니고 있는 여러 가지 측면 가운데서 특정한 측면만을 가려내어 포착하는 것이다.
- 추상화 : 클래스간의 공통점을 찾아내서 공통의 조상을 만드는 작업.
- 구체화 : 상속을 통해서 클래스를 구현, 확장하는 작업.
인터페이스 (interface)
- 일종의 추상클래스이다.
- 추상클래스(미완성 설계도)보다 추상화 정도가 높다.
- 추상메서드와 상수만을 멤버로 가질 수 있다.
- 인스턴스를 생성할 수 없고, 클래스를 작성하는데 도움을 줄 목적으로 사용된다.
- 미리 정해진 규칙에 맞게 구현하도록 표준을 제시하는데 사용된다.
인터페이스 작성
- class 대신 interface를 사용한다는 것 외에는 클래스 작성과 동일.
interface 인터페이스이름 {
public static final 타입 상수이름 = 값;
public abstract 메서드이름(매개변수);
}
- 구성요소(멤버)는 추상메서드와 상수만 가능하다.
- 모든 멤버변수는 public static final 이어야하며 이를 생략할 수 있다.
- 모든 메서드는 public abstract 이어야하며 이를 생략할 수 있다.
인터페이스 상속
- 인터페이스도 클래스처럼 상속이 가능하다.
- 클래스와 다르게 다중상속 허용된다.
interface Movable {
void move(int x, int y);
}
interface Attackable {
void attack(Unit u);
}
interface Fightable extends Movable, Attackable {}
- 인터페이스는 Object클래스와 같은 최고 조상이 없다.
인터페이스의 구현
- 인터페이스를 구현하는 것은 클래스를 상속받는 것과 같다.
- extends 대신에 implements 를 사용한다.
class 클래스이름 implements 인터페이스이름, 인터페이스이름 {
// 인터페이스에 정의된 추상메서드를 구현해야 한다.
}
- 인터페이스에 정의된 추상메서드를 완성해야 한다.
- 상속과 구현이 동시에 가능하다.
class Fighter extends Unit implements Fightable {
public void move(int x, int y) {/* 내용 구현 */}
public void attack(Unit u) {/* 내용 구현 */}
}
만일 두 개의 클래스로부터 상속을 받아야하는 상황이라면 두 조상클래스 중에서 비중이 높은 쪽을 선택하고 다른 한쪽은 클래스 내부에 포함시키는 방법으로 해결하거나 어느 한쪽의 필요한 부분을 인터페이스로 만든 다음 구현하도록 한다.
인터페이스를 이용한 다형성
인터페이스 타입의 변수로 인터페이스를 구현한 클래스의 인스턴스를 참조할 수 있다.
class Fighter extends Unit implements Fightable {
public void move(int x, int y) {}
public void attack(Fightable f) {}
}
Fighter f = new Fighter();
Fightable f = new Fightable();
인터페이스를 메서드의 매개변수 타입으로 지정할 수 있다.
void attack(Fightable f) {}
인터페이스를 메서드의 리턴타입으로 지정할 수 있다.
Fightable method() {
...
return new Fighter();
}
인터페이스의 장점
- 개발시간을 단축시킬 수 있다.
- 표준화가 가능하다.
- 서로 관계없는 클래스들에게 관계를 맺어 줄 수 있다.
- 독립적인 프로그래밍이 가능하다.
인터페이스의 이해
- 두 대상(객체)간의 연결, 대화, 소통을 돕는 중간역할을 한다.
- 선언(설계)과 구현을 분리시키는 것을 가능하게 한다.
class B {
public void method() {
System.out.println("안녕하세요");
}
}
interface I {
public void method();
}
class A implements I {
public void method() {
System.out.println("안녕하세요");
}
}
class B implements I {
public void method() {
System.out.println("안녕하세요");
}
}
- 클래스를 사용하는 쪽(User)과 클래스를 제공하는 쪽(Provider)이 있다.
- 메서드를 사용(호출)하는 쪽에서는 사용하려는 메서드의 선언부만 알고 있으면 된다.
오버라이딩
: 조상클래스로부터 상속받은 메서드를 자손클래스에 맞게 재정의 하는 것.
'Language > Java' 카테고리의 다른 글
[Java] 정리 (기초) (0) | 2022.08.20 |
---|---|
[Java] 실습 (빙고 만들기) (0) | 2022.08.19 |
[Java] 클래스(Class), 객체(Object), 인스턴스(Instanse)의 개념 (0) | 2022.08.19 |
[Java] 제어자 (modifier) (0) | 2022.08.19 |
[Java] 실습 (event) Click (0) | 2022.08.18 |