다형성 (Polymorphism)

김찬희·2023년 2월 22일
0

KH정보교육원

목록 보기
9/27

▶ 다형성

객체 지향 언어의 특징 중 하나로 '다양한 형태를 갖는다'라는 뜻으로 하나의 행동으로 여러 가지 일을 수행하는 개념.
상속을 이용한 기술로 부모 클래스 타입 참조변수 하나로 상속 관계에 있는 여러 타입의 자식 객체를 참조할 수 있는 기술

▶ 클래스 형변환

√ 업 캐스팅(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();

▶ instanceof 연산자

현재 참조형 변수가 어떤 클래스형의 객체 주소를 참조하고 있는지 확인할 때 사용
클래스 타입이 맞으면 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();
	}
}
profile
김찬희입니다.

0개의 댓글