[Java] 09_다형성(Polymorphism)

2022. 8. 9. 10:08·📗 self-study/📗 KH정보교육원 당산지원

09_다형성(Polymorphism)

 

 다형성: 많은 모양을 가지고 있는 특징 (== "상속관계 간의" 형 변환)

다형성은 전제 조건으로 상속이 깔려야 함! (중요)

 


*객체지향프로그래밍(Object Oriented Programming, OOP) 3대 요소
 => 캡슐화, 상속, 다형성
  
 *다형성
 같은 자료형에 여러 가지 객체를 대입하여 다양한 결과를 얻어내는 성질
 => "상속" 관계에서만 이루어질 수 있는 "형 변환"의 개념!

 

명심할 사항: 대입연산자 (=) 기준으로 왼쪽과 오른쪽의 자료형은 같아야 함

 

원하는 클래스 다이어그램

 

Parent 클래스

package com.kh.chap01.poly.part01.basic.model.vo;

public class Parent {
	
	// 필드부
	private int x;
	private int y;
	
	// 생성자부
	public Parent() {
		
	}
	
	public Parent(int x, int y) {
		this.x = x;
		this.y = y;
	}
	
	// 메소드부
	
	public int getX() {
		return x;
	}
	
	public void setX(int x) {
		this.x = x;
	}
	
	public int getY() {
		return y;
	}
	
	public void setY(int y) {
		this.y = y;
	}
	
	public void printParent() {
		System.out.println("나 부모야");
	}

}

Child1 클래스

package com.kh.chap01.poly.part01.basic.model.vo;

public class Child1 extends Parent {
	
	// 필드부
	private int z;
	
	// 생성자부
	public Child1() {
		
	}
	
	public Child1(int x, int y, int z) {
		super(x, y);
		this.z = z;
	}
	
	// 메소드부
	public int getZ() {
		return z;
	}
	
	public void setZ(int z) {
		this.z = z;
	}
	
	public void printChild1() {
		System.out.println("나 첫 번째 자식이야");
	}

}

Child2 클래스

package com.kh.chap01.poly.part01.basic.model.vo;

public class Child2 extends Parent {
	
	// 필드부
	private int n;
	
	// 생성자부
	public Child2() {
		
	}
	
	public Child2(int x, int y, int n) {
		super(x, y);
		this.n = n;
	}
	
	// 메소드부
	public int getN() {
		return n;
	}
	
	public void setN(int n) {
		this.n = n;
	}
	
	public void printChild() {
		System.out.println("나 두 번째 자식이야");
	}
	

}

PolyRun 클래스

package com.kh.chap01.poly.part01.basic.run;

import com.kh.chap01.poly.part01.basic.model.vo.Child1;
import com.kh.chap01.poly.part01.basic.model.vo.Parent;

public class PolyRun {

	public static void main(String[] args) {
    
    // 1. 부모 타입의 참조변수(주소값이 들어가는 stack영역의 상자)로 부모 객체(heap 영역의 알맹이)를 다루는 경우
		System.out.println("1. 부모 타입의 참조변수로 부모 객체를 다루는 경우");
		Parent p1 = new Parent();
		//  Parent 주소갑 타입 = heap 영역 실물 타입
		p1.printParent();
		// p1.printChild1();
		// => p1 참조 변수로 Parent 객체에만 접근 가능
        
// 출력물
1. 부모 타입의 참조변수로 부모 객체를 다루는 경우
나 부모야
// 2. 자식 타입의 참조변수로 자식 객체를 다루는 경우
		System.out.println("2. 자식 타입의 참조변수로 자식 객체를 다루는 경우");
		Child1 c1 = new Child1();
		// Child1 주소값 타입 = heap 영역 실물 타입
		c1.printChild1();
		c1.printParent();
		// => c1 참조변수로 Child1, Parent 둘 다 접근 가능
        
// 출력물
2. 자식 타입의 참조변수로 자식 객체를 다루는 경우
나 첫 번째 자식이야
나 부모야
// 3. 부모 타입 참조변수로 자식 객체를 다루는 경우(다형성이 적용된 개념)
		System.out.println("3. 부모 타입 참조변수로 자식 객체를 다루는 경우(다형성이 적용된 개념)");
		Parent p2 = /* (Parent) */ new Child1(); // 자동 형변환으로 자식 객체가 부모 타입 참조변수에 담김
		// 대입 연산자 기준으로 양쪽의 자료형이 다름에도 불구하고 오류 발생하지 않음
		// Child1 타입의 객체가 Parent형으로 "자동 형변환"이 되고 있음
		// => "상속" 구조에서는 클래스간의 형 변환이 가능함
		
		p2.printParent(); // p2 참조변수로 Parent에만 접근 가능
		// (Child1)p2.printChild1(); 로 진행할 경우 우선 순위 때문에 여전히 오류가 남!
		((Child1)p2).printChild1(); // 단, Child1형으로 강제 형변환 한 후 Child1에 접근 가능
		
		// 자식이 부모 되는 건 자동 형변환, 부모가 자식 되려면 강제 형변환
        
// 출력물
3. 부모 타입 참조변수로 자식 객체를 다루는 경우(다형성이 적용된 개념)
나 부모야
나 첫 번째 자식이야

 "상속" 구조에서 클래스 간의 "형 변환"이 가능함
 1. UpCasting: 자식타입 => 부모타입(자동 형변환, 형변환연산자 생략 가능) 자부자
 2. DownCasting: 부모타입 => 자식타입(강제 형변환, 형변환연산자 명시적으로 작성해야 함) 부자강

 

다형성을 배워야 하는 이유에 대한 예시

// Child1 객체 2개, Child2 객체 2개가 필요한 상황이라고 가정
		Child1[] arr1 = new Child1[2];
		arr1[0] = new Child1(1, 2, 4);
		arr1[1] = new Child1(2, 1, 5);
		
		Child2[] arr2 = new Child2[2];
		arr2[0] = new Child2(5, 7, 2);
		arr2[1] = new Child2(2, 3, 5);
		
		// 단, 다형성이 적용되면 부모 타입의 참조변수로
		// 다양한 자식객체를 받아줄 수 있음
		
		System.out.println("===== 다형성을 적용한 객체 배열 =====");
		Parent[] arr = new Parent[4]; // 자식 객체에 대한 배열 선언 없이 가능해짐!
		arr[0] = new Child1(1, 2, 4);
		arr[1] = new Child2(5, 7, 2);
		arr[2] = new Child1(2, 1, 5);
		arr[3] = new Child2(2, 3, 5);
		// 부모 타입에 담길 때는 자식 타입이 자동 형변환 된 꼴 => UpCasting
		
		// arr[0].printParent();
		((Child1)arr[0]).printChild1();
		((Child2)arr[1]).printChild2();
		((Child1)arr[2]).printChild1();
		((Child2)arr[3]).printChild2();
		// 자식타입객체로써 사용하고자 할 경우에는 강제형변환으로 원산 복구 후에 사용 가능 => DownCasting
		
		// ((Child1)arr[3]).printChild1();
		// => Child2 타입인 arr[3]을 Child1으로 형 변환 하려고 해서 오류가 발생함! 
		// ClassCastException: 클래스간 형 변환이 잘못되었을 경우 발생하는 에러
		
		System.out.println("===== 반복문 이용해서 해 보기 =====");
		
		for(int i = 0; i < arr.length; i++) {
			
			// 각 인덱스별로 실제로 참조하고 있는 자식 클래스로 형 변환 후에 메소드 호출
			// (강제형변환)arr[i].필요한메소드();
			
        		   // 1. instancefo 연산자를 쓰는 방법
                   
			if(arr[i] instanceof Child1) {
				((Child1)arr[i]).printChild1();
			}
			else {
				((Child2)arr[i]).printChild2();
			}
			
		}
        
        
// 출력물
===== 반복문 이용해서 해 보기 =====
나 첫 번째 자식이야
나 두 번째 자식이야
나 첫 번째 자식이야
나 두 번째 자식이야

*instanceof 연산자

현재 참조변수가 실제로 어떤 자식 클래스를 참고하고 있는지 확인할 때 사용
[ 표현법 ]
참조변수명 instanceof 검사할클래스명

부모객체명 instanceof 부모클래스명 == true
자식객체명 instanceof 부모클래스명 == true (상속을 받았으니 부모님 거도 내 거)
부모객체명 instanceof 자식클래스명 == false (부모객체는 자식객체의 내용물에 접근 불가)
자식객체면 instanceof 자식클래스명 == true

 

두 번째 방법(instanceof 연산자를 쓰기 싫을 경우)

 

Parent 클래스

	// instanceof 연산자를 사용하기 싫을 경우 (오버라이딩 활용)
    
	public void print() {
		System.out.println("나 부모야");
	}

Child1 클래스

@Override
	public void print() {
		System.out.println("나 첫 번째 자식이야");
	}

Child2 클래스

	@Override
	public void print() {
		System.out.println("나 두 번째 자식이야");
	}

PolyRun 클래스

System.out.println("===== 반복문 이용해서 해 보기 =====");
		
		for(int i = 0; i < arr.length; i++) {
        	arr[i].print();
			// print 메소드는 "오버라이딩" 된 상태
			// => 오버라이딩 시 "자식 메소드"가 우선권을 가져 호출됨
           }
 
// 출력물
===== 반복문 이용해서 해 보기 =====
나 첫 번째 자식이야
나 두 번째 자식이야
나 첫 번째 자식이야
나 두 번째 자식이야

단, 오버라이딩을 이용하면 굳이 형변환을 안 해도 됨
=> 알아서 자식객체의 메소드로 찾아가서 호출되는 꼴이기 때문에

 

 

ElectronicController1 클래스 (다형성 적용 전)

package com.kh.chap01.poly.part02.electronic.controller;

import com.kh.chap01.poly.part02.electronic.mode.vo.Desktop;
import com.kh.chap01.poly.part02.electronic.mode.vo.NoteBook;
import com.kh.chap01.poly.part02.electronic.mode.vo.Tablet;

// 다형성을 적용시키기 전
public class ElectronicController1 {
	
	// 용산전자상가에 새로 차린 가게
	
	// 필드부: 전자제품을 진열할 수 있는 빈 상자 종류별로 한 개씩 선언
	private Desktop desk;
	private NoteBook note;
	private Tablet tab;
	
	// 메소드부: 해당 상자에 물건을 넣는 용도의 메소드들, 상자에 담긴 물건을 꺼내서 보여 주는 메소드들
	// 물건을 넣는(필드에 값을 대입하는) 용도의 메소드들
	// setter 메소드의 원리 적용
	
	// Desktop을 넣는 메소드
	public void insert(Desktop d) { // insert: 입력하다, 넣다
		// Desktop d = new Desktop("삼성", "데스크탑", 1200000, "Geforce 1070");
		desk = d;
	}
	
	// NoteBook을 넣는 메소드
	public void insert(NoteBook n) { // 오버로딩 적용 가능
		note = n;
	}
	
	// Tablet을 넣는 메소드
	public void insert(Tablet t) {
		tab = t;
	}
	
	// 상자에 담긴 물건을 꺼내서 보여 주는 (필드에 담긴 값을 리턴) 용도의 메소드
	// getter 메소드의 원리 적용
	
	// Desktop 객체를 꺼내서 보여 주는 메소드
	public Desktop selectDesktop() { // select: 조회하다
		return desk;
	}
	
	// NoteBook 객체를 꺼내서 보여 주는 메소드: 이번 경우에는 오버로딩 적용 불가
	public NoteBook selectNoteBook() { // select: 조회하다
		return note;
	}
		
	// Tablet 객체를 꺼내서 보여 주는 메소드
	public Tablet selectTablet() { // select: 조회하다
		return tab;
	}
	

}

ElectronicRun 클래스

package com.kh.chap01.poly.part02.electronic.run;

import com.kh.chap01.poly.part02.electronic.controller.ElectronicController1;
import com.kh.chap01.poly.part02.electronic.mode.vo.Desktop;
import com.kh.chap01.poly.part02.electronic.mode.vo.NoteBook;
import com.kh.chap01.poly.part02.electronic.mode.vo.Tablet;

public class ElectronicRun {

	public static void main(String[] args) {
		
		// 1. 다형성을 적용하기 전(ElectronicController1)
		ElectronicController1 ec = new ElectronicController1();
		
		// 이 시점 기준에서 ec.desk == null, ec.note == null, ec.tab == null
		// => 우리 가게에 빈 상자가 세 개 생긴 꼴
		
		// ec.desk = new Desktop("삼성", "데탑", 1200000, "Geforce 1070");
		// => is not visible: private 접근 제한자이므로 직접 접근 불가함
		// => desk라는 상자는 private이라 직접 접근 불가하므로 우회해서 간접 접근 해야 함 
		
		/* 이렇게 담아도 되고
		Desktop d = new Desktop("삼성", "데탑", 1200000, "Geforce 1070");
		ec.insert(d);
		*/
		
		// 이렇게 담아도 됨!
		ec.insert(new Desktop("삼성", "데탑", 1200000, "Geforce 1070"));
		
		ec.insert(new NoteBook("엘지", "그램", 2000000, 4));
		
		ec.insert(new Tablet("애플", "아이패드", 500000, false));
		
		// => 여기까지 물걸을 각각 납품받아서 상자에 담아 준 꼴
		
		// 손님에게 제품등을 상자로부터 꺼내서 보여 주기
		Desktop d = ec.selectDesktop(); // 안에서 바깥으로 꺼냄
		NoteBook n = ec.selectNoteBook();
		Tablet t = ec.selectTablet();
		
		System.out.println(d); // 꺼내 온 상자 보여 주기
		System.out.println(n); // 원래는 toString이 주소값을 호출하는 메소드이지만 
		System.out.println(t); // 우리는 오버라이딩을 통해 toString을 내용 표출로 바꿨으므로 그렇게 나옴
		
	}

}

// 출력물
brand: 삼성, name: 데탑, price: 1200000, graphic: Geforce 1070
brand: 엘지, name: 그램, price: 2000000, usbPort: 4
brand: 애플, name: 아이패드, price: 500000, penFlag: false

	
	// 출력물 잘 나오긴 하지만 만들 상자가 아주 많아요...

ElectronicController2 클래스 (다형성 적용 후)

package com.kh.chap01.poly.part02.electronic.controller;

import com.kh.chap01.poly.part02.electronic.mode.vo.Electronic;

// 다형성을 적용시켰을 때
public class ElectronicController2 {
	
	// 용산전자상가에 다시 새롭게 차린 가게
	
	// 필드부
	//어느 물건이든지 넣을 수 있는 3칸짜리 창고
	private Electronic[] elec = new Electronic[3];
	
	// 메소드부
	// 창고에 물건을 넣는 메소드: 뭐를 어디에 넣을 건지?
	public void insert(Electronic any, int index) { // 어느 전자제품이 들어오든지, 창고에 넣을 건데
		elec[index] = any;                          // 그 들어오는 전자제품은 창고에 순서대로 넣을 거야
	}
	
	// 창고로부터 물건을 하나 꺼내오는 메소드
	public Electronic select(int index) { // 전자제품의 순서를 조회할 건데
		return elec[index];               // 내가 원하는 창고 순서에 있는 애를 꺼내 와 줘
	}
	
	// 창고에 있는 물건 모두를 다 보여 주는 메소드
	public Electronic[] selct() { // 전자제품 조회할 거야
		return elec;              // 전자제품 다 들고와~
	}
	

}

ElectronicRun 클래스

// 2.다형성을 적용했을 경우 (ElectonicController2)
		ElectronicController2 ec2 = new ElectronicController2();
		
		ec2.insert(new Desktop("삼성", "데탑", 1200000, "Geforce 1070"), 0);
		ec2.insert(new NoteBook("엘지", "그램", 2000000, 4), 1);
		ec2.insert(new Tablet("애플", "아이패드", 500000, false), 2);
		
		/*
		// 창고에 담긴 물건들을 하나씩 뽑아 보기
		Desktop d = (Desktop)(ec2.select(0)); // 부모 클래스가 자식 클래스로 되기 위해서는 강제 형변환 해야 함
		NoteBook n = (NoteBook)(ec2.select(1));
		Tablet t = (Tablet)(ec2.select(2));
		
		System.out.println(d);
		System.out.println(n);
		System.out.println(t);
		*/
		
		// 반복문 활용해서 물건 꺼내오기
		Electronic[] elec = ec2.select(); // 창고 전체를 꺼내 옴
		
		for(int i = 0; i < elec.length; i++) {
			System.out.println(elec[i] /* .toString() */);
			// 오버라이딩 했기 때문에 굳이 형 변환 할 필요 없음! 오버라이딩 한 자식 클래스가 우선권 가지니까
		}

 

상속 관계에 있다면 다형성을 통해 자료형이 달라도 같은 배열에 넣을 수 있음!

 

*다형성을 사용하는 이유
 1. 부모 타입의 객체 배열로 다양한 자식 객체들을 모아서 담아둘 수 있음
 2. 메소드에 매개 변수나 반환형에 다형성을 적용하게 되면 메소드 개수가 줄어듦

저작자표시 (새창열림)
'📗 self-study/📗 KH정보교육원 당산지원' 카테고리의 다른 글
  • [Java] 10_기본 API(Math, StringPool, StringMethod, StringTokenizer, Wrapper-parsing, Date)
  • [Java] 09_다형성(추상 클래스와 인터페이스)
  • [Java] 08_상속(Inheritance, 오버라이딩)
  • [Java] 06_객체(생성자, 메소드, 오버로딩), 07_객체 배열(oneVsmany)
천재강쥐
천재강쥐
  • 천재강쥐
    디버거도 버거다
    천재강쥐
  • 전체
    오늘
    어제
    • Category (467)
      • 진짜 너무 궁금한데 이걸 나만 몰라...? (0)
      • 💾 Portfolio (2)
      • 🐤 CodingTest (28)
        • Java (20)
        • ᕕ(ꐦ°᷄д°᷅)ᕗ❌ (5)
      • 🚀 from error to study (142)
        • AI (1)
        • Cloud (2)
        • DB (12)
        • Front-End (16)
        • Github (14)
        • Java (39)
        • Mac (7)
        • Normal (29)
        • Server (22)
      • 📘 certificate (44)
        • 📘 리눅스마스터1급 (1)
        • 📘⭕️ 정보처리기사 (40)
        • 📘⭕️ SQLD (3)
      • 📗 self-study (234)
        • 📗 inflearn (35)
        • 📗 생활코딩 (8)
        • 📗 KH정보교육원 당산지원 (190)
      • 🎨 Scoop the others (0)
        • 📖 Peeking into other people.. (0)
        • 🇫🇷 (0)
        • 📘⭕️ 한국사능력검정시험 심화 (11)
        • 오블완 (4)
  • 인기 글

  • hELLO· Designed By정상우.v4.10.1
천재강쥐
[Java] 09_다형성(Polymorphism)
상단으로

티스토리툴바