* 다형성(polymorphism)
- 여러 가지 형태를 가질 수 있는 능력
= 조상 타입 참조 변수로 자손 타입 객체를 다루는 것.
조상 타입 참조변수 = new 자식타입(); 타입 불일치. 즉 참조 변수의 타입과 인스턴스(객체) 타입이 불일치.
// 부모인 Tv엔 5개의 멤버, 자식인 SmartTv엔 상속받은 5개 + 2개의 멤버 가 있다고 가정.
SmartTv s = new SmartTv(); // s 를 통해 7개 멤버 모두 접근 가능
Tv t = new SmartTv(); // t 를 통해 부모의 멤버인 5개만 접근 가능
반대의 경우 SmartTv s = new Tv(); 는 안 됨.
* 참조변수의 형 변환
- 사용할 수 있는 멤버의 개수를 조절하는 것 (기본형 형 변환처럼 값이 변경되는 게 아님)
- 조상, 자손 관계의 참조변수만 서로 형 변환 가능
// 부모 class Car --> 멤버 4개
// 자식 class FireCar extends Car --> 멤버 1개 (총 5개)
// 자식 class Ambulance extends Car --> 멤버 1개 (총 5개)
FireCar f = new FireCar();
Car c = (Car) f;
FireCar f2 = (FireCar) c;
Ambulance a = (Ambulance) f2; // 부모 자식 관계가 아니므로 에러

* instanceof 연산자
- 참조변수의 형 변환 가능여부 확인에 사용. 가능하면 true 반환. 형 변환 전 반드시 확인 필요
- 자신의 모든 조상으론 형변환 가능함.
void doWork(Car c){
if(c instanceof FireEngine){ //형변환 가능 확인
fireEngine fe = (fireEngine)c; // 형변환
fe.water();
}
}
* 다형성의 장점
1. 다형적 매개변수
- 참조형 매개변수는 메서드 호출 시, 자신과 같은 타입 또는 자손타입의 인스턴스를 넘겨줄 수 있다.
class Procudts{
int price;
}
class Tv extends Products{}
class Dvd extends Products{}
class Computer extends Produts{}
class Customer{
int money = 1000;
void buy(Products p){
money -= p.price;
}
Products p1 = new Tv();
products p2 = new Dvd();
products p3 = new Computer();
Customer c = new Customer();
c.buy(p1);
c.buy(p2);
c.buy(p3);
위 코딩을 보면, Customer 클래스의 buy 메서드에서 매개변수를 Products 형으로 받았는데, 이는 다형성이 가능하기 때문이다.
Tv, Dvd, Computer를 전부 Products형 참조변수로 생성했기 때문에 buy 메서드에 넣을 수 있다.
만약 다형성이 없었다면 buy 메서드를 오버로딩을 통해 모든 제품 타입을 매개변수로 갖도록 만들어야 했을 것이다.
class Product{
int price;
int bonuspoint;
Product(){}
Product(int i){
this.price = i;
this.bonuspoint = (int)(i/10.0);
}
}
class Tv1 extends Product{
Tv1(){super(100);}
public String toString() {return "Tv";} // 오버라이딩
}
class Computer extends Product{
Computer(){super(200);}
public String toString() {return "Computer";}
}
class Buyer {
int Money;
int Bonuspoint;
Buyer(int a){
this.Money = a;
this.Bonuspoint = 0;
}
void buy(Product p) {
if(this.Money < p.price) {
System.out.println("잔액이 부족합니다");
return;
}
this.Money -= p.price;
this.Bonuspoint += p.bonuspoint;
System.out.println(p + "을 구매하셨습니다.");
}
}
public class Ex7 {
public static void main(String args[]) {
Buyer b = new Buyer(10000);
Tv1 Tv = new Tv1();
Computer C = new Computer();
b.buy(Tv);
b.buy(C);
System.out.println(b.Money);
}
}
강의 듣다 조금 이해가 안 돼서 예제를 직접 코딩해봤다.
마지막 buy 메서드를 사용할 때 하나 이해 안 가는 게 있는데,
위 코딩처럼 Tv, C 를 각각 Tv1, COmputer 형으로 작성해도 buy메서드를 사용하는데 문제없다.
buy 메서드는 매개변수로 Product형을 받도록 돼있는데... 자동으로 형 변환이 되는 걸까?
2. 하나의 배열로 여러 종류 객체 다루기
Product p[] = new Product[3];
p[0] = new Tv();
p[1] = new Computer();
p[2] = new Audio();
* 추상 클래스(abstract class)
- 미완성 메서드를 갖고 있는 클래스
abstract class Player{
abstract void play(int pos);
abstract void stop();
}
상속을 통해 추상 메서드를 완성 후 인스턴스시(객체) 생성 가능.
언제 쓰는가? 꼭 필요하지만 자손마다 다르게 구현될 것으로 예산되는 경우.
객체 생성은 안되지만, 클래스 내에서 호출은 가능함.
abstract class Player{
boolean pause;
int currentPos;
Player(){
pause = false;
currentPos = 0;
}
abstract void play(int pos);
abstract void stop();
void play(){
play(currentPos);
}
}
im play에서 play 추상 메서드 호출 가능.
기존 클래스의 공통부분을 뽑아서 추상 클래스를 만들 수 있음.
* 인터페이스(interface)
- 추상 메서드의 집합 / 구현된 것이 없는 설계도. 껍데기(메서드 : 전부 public abstract / 상수 : 전부 public static final)
인터페이스의 상속
- Object가 최고 조상 아님, 인터페이스의 조상은 인터페이스만 가능
- 다중 상속이 가능함. (구현부가 없어 충돌 발생할 수 없음)
인터페이스의 구현
- 인터페이스에 정의된 추상 메서드를 완성하는 것. extends 가 아니라 implements 사용
- 일부만 구현하는 경우, 클래스 앞에 abstract를 붙여야 함.
interface Fightable{
void move(int x, int y);
void attack(Unit u);
}
class Fighter implements Fightable{
public void move(int x, int y){/* 내용 생략*/}
public void attach(Unit u){/*내용 생략*/}
}
추상 클래스 VS 인터페이스
| 추상클래스 | 인터페이스 | |
| 추상메서드 | O | O |
| iv | O | X |
| im | O | X |
| 생성자 | O | X |
* 인터페이스를 이용한 다형성
클래스의 다형성과 마찬가지로, 조상형 참조변수 = new 자식 객체 가능하다.
Fightable f = new Fighter();
매개변수를 interface형으로 갖는 경우. 해당 인터페이스를 구현한 클래스의 인스턴스가 들어갈 수 있다!
interface Fightable{
void move(int x, int y);
void attack(Fightable f); // Fightable 인터페이스 구현한 클래스의 인스턴스만 들어갈 수 있음
}
메서드의 리턴 타입으로 인터페이스 지정 가능.
Fightable testmethod(){ // Fighterable인터페이스 구현한 클래스의 인스턴스(객체) 반환
Fighter f = new Fighter();
return f;
}
//위 매서드 호출 시 형일치 해야함
Fightable f = testmethod();
/* 내가 이해한 다형성 */l
다양한 클래스들 중 동일한 성질?(메서드)를 가진걸 하나의 조상으로 묶는 느낌?
그런데 클래스의 경우 조상을 1개만 가질 수 있어 1개의 분류만 가능하지만,
인터페이스는 다중 상속이 가능하므로 다양한 분류로 묶을 수 있다.
abstract class Unitt{
abstract void move(int x, int y);
void stop() {System.out.println("멈춥니다.");}
}
interface Fightable{
void move(int x, int y);
void fignt(Fightable f);
}
class Fighter extends Unitt implements Fightable{
String i;
Fighter(){}
Fighter(String S){this.i = S;}
public void move(int x, int y) {
System.out.println("["+x+","+y+"] 로 이동");
}
public void fignt(Fightable f) {
System.out.println(f+"를 공격합니다.");
}
public String toString() {
return i;
}
}
public class FighterTest {
public static void main(String[] args) {
Fighter f = new Fighter();
f.move(100, 200);
Fighter k = new Fighter("이름");
f.fignt(k);
}
}
예시를 구현해봤다. Fighter 생성 시 이름을 부여했다.
* 인터페이스의 장점
1. 두 대상(객체) 간의 연결을 돕는 '중간 역할'을 한다.
2. 선언(설계)과 구현을 분리시킬 수 있게 한다.
Class B{ // 선언(설계) + 구현
public void method(){
System.out.printlf("methodInB");
}
}
----------------------------------------------------------------------------------------------
interface I{ // 선언(설계)
void method();
}
class B implements I{ // 구현
public void method(){
System.out.printlf("methodInB");
}
}
위 코딩처럼 선언과 구현을 분리할 수 있음. 수정이 필요할 때 구현 부위를 수정하면 돼서 훨씬 유연함.
3. A의 구현부를 B에서 C로 변경할 때, 인터페이스가 있다면 A 변경 없이 B --> C 변경만 진행하면 됨. (느슨한 결합)
// A 와 B 직접적인 연결
class A{
public void metgodA(B b){ // B 사용 --> B 를 C로 수정할 때 변경 필요.
b.metbodB();
}
}
class B{
public void methodB(){
System.out.println("methodB");
}
}
class InterfaceTest{
public static void main(String args[]){
A a = new A();
a.methodA(new B());
}
}
------------------------------------------------------------------------------------------------
// interface를 통해 A 와 B 간접적인 연결 (느슨한 연결)
class A{
public void methodA(I i){ // interface b 사용 --> B를 C로 수정해도 변경 필요 없음.
i.methodB();
}
}
interface I{
void methodB();
}
class B implements I(
public void methodB(){
System.out.println("methodB");
}
}
class InterfaceTest{
public static void main(String args[]){
A a = new A();
a.methodA(new B());
}
}
조금만 복잡해져도 느슨한 연결이 훨씬 유연하게 수정 가능할 것 같다.
4. 개발 시간 단축 가능
5. 변경에 유리한 유연한 설계
6. 표준화가 가능!
7. 서로 관계없는 클래스들을 관계를 맺어줄 수 있다. 새로운 인터페이스를 만들어 하나로 묶어줌!

* 인스턴스에서 사용되는 디폴트 메서드와 static 메서드
- 인터페이스에 새로운 메서드를 추가하기 어려움. (추가 시 자식들 모두 변경 필요)
--> 해결책 : 디폴트 메서드
interface MyInterface{
void method();
void newMethod();
}
interface MyInterface{
void method();
default void newMethod(){} // default 적어줘야함
}
but 이렇게 새로 추가가 생기면서 충돌 문제가 발생
--> 충돌 발생 시 오버라이딩을 통해 구현 필요한 것으로 수정
'코딩 > JAVA 공부' 카테고리의 다른 글
| [JAVA] 자바의 정석 _ 예외 (try-catch 문) (0) | 2022.10.11 |
|---|---|
| [JAVA] 자바의 정석 _ 내부 클래스 (0) | 2022.10.11 |
| [JAVA] 자바의 정석 _ 상속, 포함 (1) | 2022.10.08 |
| [JAVA] 자바의 정석 _ 메서드 (0) | 2022.10.07 |
| [JAVA] 자바의 정석 _ 객체지향 언어 (0) | 2022.10.07 |