객체 지향 언어의 특징 중 하나로 '다양한 형태를 갖는다'라는 뜻으로 하나의 행동으로 여러 가지 일을 수행하는 개념.
상속을 이용한 기술로 부모 클래스 타입 참조변수 하나로 상속 관계에 있는 여러 타입의 자식 객체를 참조할 수 있는 기술
√ 업 캐스팅(Up Casting)
상속 관계에 있는 부모, 자식 클래스 간에 부모타입의 참조형 변수가 모든 자식 타입 객체의 주소를 참조할 수 있음
//Sonata 클래스는 Car 클래스의 후손
Car c = new Sonata();
//Sonata클래스형에서 Car클래스형으로 바뀜
√ 다운 캐스팅(Down Casting)
자식 객체의 주소를 받은 부모 참조형 변수를 가지고 자식의 멤버를 참조해야 할 경우, 부모 클래스 타입의 참조형 변수를 자식 클래스 타입으로 형변환 하는 것
자동으로 처리되지 않기 때문에 반드시 자식 타입을 명시하여 형변환
//Sonata 클래스는 Car 클래스의 후손
Car c = new Sonata();
((Sonata)c).moveSonata();
다형성을 이용하여 상속 관계에 있는 하나의 부모 클래스 타입의 배열 공간에 여러 종류의 자식 클래스 객체 저장 가능
Car[] carArr = new Car[5];
carArr[0] = new Sonata();
carArr[1] = new Avante();
carArr[2] = new Grandure();
carArr[3] = new Spark();
carArr[4] = new Morning();
다형성을 이용하여 메소드 호출 시 부모타입의 변수 하나만 사용해 자식 타입의 객체를 받을 수 있음
public void execute() {
driveCar(new Sonata());
driveCar(new Avante());
driveCar(new Grandure());
}
public void driveCar(Car c) {}
실제 실행할 메서드 코드와 호출하는 코드를 연결시키는 것
√ 정적 바인딩
프로그램이 실행되기 전 컴파일 단계에서 메서드와 메서드 호출부를 연결
√ 동적 바인딩
컴파일 시 정적 바인딩된 메서드를 실행할 당시의 객체 타입을 기준으로 바인딩 되는 것
√ 동적 바인딩 성립 요건
상속 관계로 이루어져 다형성이 적용된 경우, 메소드 오버라이딩이 되어 있으면 정적으로 바인딩 된 메소드 코드보다 오버라이딩 된 메소드 코드를 우선적으로 수행
이클립스에서 검출해주지 않기 때문에 잘 예상/예측해서 사용해야 함
Car c = new Bentley();
c.drive();
현재 참조형 변수가 어떤 클래스형의 객체 주소를 참조하고 있는지 확인할 때 사용
클래스 타입이 맞으면 true, 맞지 않으면 false 반환
√ 표현식
if(레퍼런스 instanceof 클래스타입) {
//true일때 처리할 내용, 해당 클래스 타입으로 down casting
}
if(c instanceof Sonata) {
((Sonata)c).moveSonata();
} else if (c instanceof Avante) {
((Avante)c).moveAvante();
} else if (c instanceof Grandure) {
((Grandure)c).moveGrandure();
}
추상화 : 공통점 추출(구체적 X), 구체적인 것을 제거
√ 추상 클래스(abstact class)
몸체 없는 메서드(추상 메서드)를 포함한 클래스 (미완성 설계도)
추상 클래스일 경우 클래스 선언부에 abstract 키워드 사용
[접근제한자] abstract class 클래스명 {}
√ 추상 메서드(abstract method)
몸체 없는 메서드
추상 메서드의 선언부에 abstract 키워드 사용
상속 시 반드시 구현해야 하는, 오버라이딩이 강제화되는 메서드
[접근제한자] abstract 반환형 메서드명(자료형 변수명);
√ 특징
1. 미완성 클래스(abstract 키워드 사용)
자체적으로 객체 생성 불가 → 반드시 상속하여 객체 생성
2. abstract 메서드가 포함된 클래스는 반드시 abstract 클래스
abstract 메서드가 없어도 abstract 클래스 선언 가능
→ 완성된 클래스 이지만 객체로 만들지 못하게 하겠다
3. 클래스 내에 일반 변수, 메서드 포함 가능
4. 객체 생성은 안되지만 참조형 변수 타입으로는 사용 가능
√ 장점
상속 받은 자식에게 공통된 멤버 제공.
일부 기능의 구현을 강제화(공통적이나 자식 클래스에 따라 재정의 되어야 하는 기능).
상수형 필드와 추상 메서드만을 작성할 수 있는 추상 클래스의 변형체
메서드의 통일성을 부여하기 위해 추상 메서드만 따로 모아놓은 것으로 상속 시 인터페이스 내에 정의된 모든 추상메서드 구현해야 함
[접근제한자] interface 인터페이스명 {
// 상수도 멤버로 포함할 수 있음
public static final 자료형 변수명 = 초기값;
// 추상 메서드만 선언 가능
[public abstract] 반환자료형 메서드명([자료형 매개변수]);
// public abstract가 생략되기 때문에
// 오버라이딩 시 반드시 public 표기해야 함
√ 특징
1. 모든 인터페이스의 메서드는 묵시적으로 public abstract
2. 변수는 묵시적으로 public static final
3. 객체 생성은 안되나 참조형 변수로는 가능(다형성)
√ 장점
다형성을 이용하여 상위 타입 역할(자식 객체 연결)
인터페이스 구현 객체에 공통된 기능 구현 강제화(== 구현 객체간의 일관성 제공)
공동 작업을 위한 인터페이스 제공
package edu.kh.polymorphism.ex2.dto;
// 추상 클래스(abstract class)
// - 추상 메서드가 포함된 클래스
// 단, 추상 메서드가 없어도 추상 클래스가 될 수 있다!
public abstract class Animal /* extends Object */{
// -> 미작성 시 컴파일러가 자동 추가
// 추상화, 캡슐화
// 필드
private String type; // 종(양서류, 포유류, 파충류...)
private String eatType; // 식성(육식, 초식, 잡식...)
// 생성자
public Animal() { // 기본 생성자
super(); // 부모 생성자 호출
// 미작성 시 컴파일러가 자동 추가
}
public Animal(String type, String eatType) { // 매개 변수 생성자
this.type = type;
this.eatType = eatType;
}
// 메서드(+getter/setter)
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getEatType() {
return eatType;
}
public void setEatType(String eatType) {
this.eatType = eatType;
}
@Override
public String toString() {
// 객체가 가지고 있는 필드를 하나의 문자열로 반환하는 용도의 메서드
// (자식이 오버라이딩해서 사용하길 권장)
// 오버라이딩 X -> 패키지명+클래스명@해시코드
return type + " / " + eatType;
}
// 추상 메서드 (미완성된 메서드)
// -> 상속 받은 자식 클래스에 반드시 오버라이딩 해야한다!(오버라이딩 강제화)
// --> 추상 메서드를 포함한 클래스는 반드시 추상 클래스여야만 한다!
public abstract void breath();
}
package edu.kh.polymorphism.ex2.dto;
public class Person extends Animal{
// 추상 클래스 상속 시
// abstract 메서드도 상속이 되는데
// 미완성 상태이므로 구현(오버라이딩)하라고 에러 발생
private String name;
private int age;
public Person() {} // 기본 생성자
public Person(String type, String eatType, String name, int age) { // 매개변수 생성자
super(type, eatType); // 부모의 매개변수 생성자 호출
this.name = name;
this.age = age;
}
// alt + [shift] + s -> r
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
// alt + [shift] + s -> s
@Override
public String toString() {
// super : 부모 참조 변수
return super.toString() + " / " + name + " / " + age;
}
// 상속 받은 추상 메서드
@Override
public void breath() {
System.out.println("코와 입으로 숨을 쉰다.");
}
}
package edu.kh.polymorphism.ex2.dto;
public class Fish extends Animal {
private int fin; // 지느러미 개수
public Fish() {}
// alt + shift + s -> o
public Fish(String type, String eatType, int fin) {
super(type, eatType);
this.fin = fin;
}
public int getFin() {
return fin;
}
public void setFin(int fin) {
this.fin = fin;
}
@Override
public String toString() {
return super.toString() + " / " + fin;
}
@Override
public void breath() {
System.out.println("아가미 호흡을 합니다.");
}
}
package edu.kh.polymorphism.ex2.dto;
public interface KH { // 인터페이스(접점)
// 상수형 필드 (public static final)만 작성 가능
/* 인터페이스는 무조건 상수형 필드만 작성 가능하기 때문에
* 상수를 나타내는 키워드 public static final을 생략해도
* 작성된걸로 인식된다.
* == 인터페이스 필드는 묵시적(암묵적)으로 public static final
* */
/* public static final */ String KH_ADDRESS = "서울시 중구 남대문로 120 2층";
int A = 10;
// 추상 메서드
// - 인터페이스는 추상클래스의 변형체로
// 모든 메서드가 public abstract (추상 메서드)이다
// == 인터페이스의 모든 메서드는 묵시적으로 public abstract (추상 메서드)
// 단, default 메서드, static 메서드는 abstract가 아님.
/* public abstract */ void lesson(); // 수업
}
package edu.kh.polymorphism.ex2.dto;
// 클래스 상속 : extends (확장하다)
// -> 부모가 가지고 있는 필드와 완성된 메서드를 물려 받아 객체 크기가 확장됨
// 인터페이스 상속 : implements (구현하다)
// -> 추상 메서드만 물려받아 강제로 해당 메서드를 구현(오버라이딩) 해야함
public class HongGilDong implements KH {
@Override
public void lesson() {
System.out.println("A강의장에서 자바 수업 참여");
}
}
package edu.kh.polymorphism.ex2.dto;
public class KimSamSun implements KH{
@Override
public void lesson() {
System.out.println("G강의장에서 보안 수업 참여");
}
}
package edu.kh.polymorphism.ex2.dto;
// 계산기 인터페이스
// -> 모든 계산기에 대한 공통 필드, 기능명을 제공하는 용도
// -> 공통 규약(이름만) 설정
public interface Calculator {
public static final double PI = 3.14;
final int MAX_NUM = 100_000_000; // 1억
int MIN_NUM = -100_000_000; // -1억
// alt + shift + j
/** 두 정수의 합 반환
* @param a
* @param b
* @return a와 b의 합
*/
public abstract int plus(int a, int b); // 더하기
/** 두 정수의 차 반환
* @param a
* @param b
* @return a와 b의 차
*/
int minus(int a, int b); // 빼기
/** 두 정수의 곱 반환
* @param a
* @param b
* @return a와 b의 곱
*/
int multiple(int a, int b); // 곱하기
/** 두 정수의 몫 반환
* @param a
* @param b
* @return a와 b를 나눈 몫
*/
int divide(int a, int b);
/** 나누기 소수점까지 연산
* @param a
* @param b
* @return 실수값
*/
double divide2(int a, int b);
/** 원의 넓이 반환
* @param r
* @return 원의 넓이
*/
double areaOfCircle(double r); // 원의 넓이
/** a의 x 거듭 제곱 (a^x)
* @param a
* @param x
* @return (a^x)
*/
int square(int a, int x);
}
package edu.kh.polymorphism.ex2.dto;
// 각자 계산기 구현하기
public class KCHCalculator implements Calculator, KH{
@Override
public int plus(int a, int b) {
return a + b;
}
@Override
public int minus(int a, int b) {
return a - b;
}
@Override
public int multiple(int a, int b) {
return a * b;
}
@Override
public int divide(int a, int b) {
return a / b;
}
@Override
public double divide2(int a, int b) {
return (double)a / b;
}
@Override
public double areaOfCircle(double r) {
// return Calculator.PI * r * r; // (정확한 표기법)
return PI * r * r; // (상속 받은 PI를 자식이 자기 것처럼 사용)
}
@Override
public int square(int a, int x) {
int b = 1;
for(int i=0;i<x;i++) {
b *= a;
}
return b;
}
@Override
public void lesson() {
// TODO Auto-generated method stub
}
// 재귀호출
// @Override
// public int square(int a, int x) {
// if(x == 1){
// return a;
// }
// return a * square(a, x-1);
// }
}
package edu.kh.polymorphism.ex2.service;
import edu.kh.polymorphism.ex2.dto.*; // dto 패키지의 모든 클래스 import
// * (별, 애스터리스크) : 모든, ALL
public class TestService {
public void ex1() {
// 1. 추상 클래스는 진짜로 객체로 못만들까?
// Animal a = new Animal();
// Cannot instantiate the type Animal : 객체화 할 수 없는 타입이다.
// -> 추상 클래스이기 때문에
// 2. Animal을 상속 받은 자식 객체 생성
Person p1 = new Person("포유류", "잡식", "홍길동", 25);
p1.breath();
// 3. 추상 클래스는 객체 생성 X / 부모 타입 참조 변수 O
// (다형성의 업캐스팅)
Animal a1 = new Person("포유류", "채식", "김건강", 30);
a1.breath();
// 동적 바인딩에 의해 Person에 구현된 breath()가 호출됨
// 객체배열 + 추상 클래스
Animal[] arr = new Animal[3];
// Animal 객체 참조 변수의 묶음을 생성한 것
// (Animal 객체는 만들 수 없다!)
arr[0] = new Fish();
arr[1] = new Person();
arr[2] = new Fish();
System.out.println("---------------------------");
// 향상된 for문
for(Animal a : arr) {
a.breath();
}
}
public void ex2() {
// 인터페이스
// 1. 인터페이스는 상수형 필드(public static final)만 작성 가능
// - 인터페이스에 작성했지만 클래스에 작성하는 static 필드와 사용법 동일
// - 인터페이스 필드는 묵시적(암묵적)으로 public static final
System.out.println(KH.KH_ADDRESS);
System.out.println(KH.A);
// 2. 인터페이스로 객체 생성 X, 부모 타입 참조 변수 O
// KH k = new KH(); // 객체 생성 X
// 객체 배열 + 다형성 + 동적바인딩
KH[] arr = new KH[2]; // KH 참조 변수의 묶음 생성
arr[0] = new HongGilDong();
arr[1] = new KimSamSun();
for(KH k : arr) {
k.lesson();
}
}
public void ex3() {
// 계산기 구현
Calculator cal = new KCHCalculator();
int a = 7;
int b = 4;
System.out.println("a = 7, b =4");
System.out.println("합 : " + cal.plus(a, b));
System.out.println("차 : " + cal.minus(a, b));
System.out.println("곱 : " + cal.multiple(a, b));
System.out.println("몫 : " + cal.divide(a, b));
System.out.println("나누기 결과(실수) : " + cal.divide2(a, b));
System.out.println("-------------------------------------");
int r = 12;
System.out.println("반지름 = " + r);
System.out.println("원의 넓이 : " + cal.areaOfCircle(r));
System.out.println("-------------------------------------");
int num = 2;
int x = 9;
System.out.println(num + "의 " + x + "제곱");
System.out.println("결과 : " + cal.square(num, x));
}
}
package edu.kh.polymorphism.ex2.run;
import edu.kh.polymorphism.ex2.service.TestService;
public class TestRun {
public static void main(String[] args) {
TestService service = new TestService();
// service.ex1();
// service.ex2();
service.ex3();
}
}