// 기억해둘 것 !! '=' 기준으로 해서 왼쪽과 오른쪽의 자료형(타입)은 같아야 됨
System.out.println("1. 부모타입 레퍼런스로 부모객체를 다루는 경우");
Parent p1 = new Parent();
p1.printParent();
// p1 레퍼런스로 Parent 에만 접근 가능
System.out.println("2. 자식타입 레퍼런스로 자식객체를 다루는 경우");
Child1 c1 = new Child1();
c1.printChild1();
c1.printParent(); // 자동형변환 된거였음!! (Child1 => Parent)
// ((Parent)c1).printParent();
// c1 레퍼런스로 Child1, Parent 둘다 접근 가능
// Parent 접근시 자동으로 형변환된 채로 진행된거임
System.out.println("3. 부모타입 레퍼런스로 자식객체를 다루는 경우(다형성)");
Parent p2 = /*(Parent)*/new Child1(); // 자료형이 다르다 ? => 에러안뜸! => 자동형변환 된거임
p2.printParent();
// p2.printChild(); => 안됨
((Child1)p2).printChild1(); // 강제 형변환을 하면 호춣은 가능!!
// p2 레퍼런스로 지금 당장은 Parent에만 접근 가능하지만
// Child1에 접근하고자 한다면 강제형변환을 하면 접근은 가능!!
상속 구조의 클래스들 간에는 형변환 가능
1. UpCasting
자식타입 -> 부모타입 형변환
자동형변환
ex) 자식.부모메소드();
2. DownCasting
부모타입 -> 자식타입 형변환
강제형변환
ex) ((자식)부모).자식메소드();
String str = (String)new Child1(); 상속구조 아니면 안된다.
Child1 Class
package com.kh.chap01_poly.part01_basic.model.vo;
public class Child1 extends Parent {
private int z;
public Child1() {
super(); // 생략돼있음. 부모 기본 생성자가 없어지면 오류남
}
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("나 첫번째 자식이야");
}
@Override
public void print() {
System.out.println("나 첫번째 자식이야");
}
}
Child2 Class
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 printChild2() {
System.out.println("나 두번째 자식이야");
}
@Override
public void print() {
System.out.println("나 두번째 자식이야");
}
}
Parent Class
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("나 부모야");
}
public void print() {
System.out.println("나 부모야");
}
}
PolyRun Class
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.Child2;
import com.kh.chap01_poly.part01_basic.model.vo.Parent;
public class PolyRun {
public static void main(String[] args) {
// 다형성 정의
// - 부모타입으로 부터 파생된 여러가지 타입의 자식 객체들을 부모클래스 타입 하나로도 다룰 수 있다.
// 다형성을 쓰는 목적
// 다형성을 배우기전..
Child1[] arr1 = new Child1[2];
arr1[0] = new Child1(1, 2, 4);
arr1[1] = new Child1(2, 3, 5);
Child2[] arr2 = new Child2[2];
arr2[0] = new Child2(2, 1, 5);
arr2[1] = new Child2(5, 7, 2);
// -----------------------------------------------------------
// 다형성(부모타입 레퍼런스)을 적용한 후..
Parent[] arr = new Parent[4];
// Parent 타입
arr[0] = new Child1(1, 2, 4);
arr[1] = new Child2(2, 1, 5);
arr[2] = new Child1(2, 3, 5);
arr[3] = new Child2(5, 7, 2);
// 하나의 부모타입만으로 여러 자식 객체들을 받을 수 있음 => 편리하다, 코드길이 감소
System.out.println("===================================");
// arr[0].printChild1(); => 안된다.
((Child1)arr[0]).printChild1();
// arr[1].printChild2(); => 안된다.
((Child2)arr[1]).printChild2();
((Child1)arr[2]).printChild1();
// ((Child2)arr[2]).printChild2(); // ClassCastException : 부적절한 형변환
// arr[3]
((Child2)arr[3]).printChild2();
System.out.println("======== 반복문 이용해서 출력 =========");
for(int i = 0; i < arr.length; i++) {
/*
* instanceof 연산자 => 연산결과 true / false 변환
* 현재 레퍼런스가 실질적으로 어떤 클래스 타입을 참조하는지 확인할 때 사용
*/
/*if(i == 0 || i == 2) {*/
if(arr[i] instanceof Child1) { // 해당 레퍼런스가 실제 Child1 참조하고 있다면 true 아니면 false
((Child1)arr[i]).printChild1();
}else {
((Child2)arr[i]).printChild2();
}
} // 양다리 => 공유 : 연상(오빠) , 차은우 : 연하(은우야)
Parent pp = new Child1();
pp.print();
/*
* 동적바인딩 : 프로그램이 실행되기 전에는 컴파일이 되면서 정적바인딩(레퍼런스 타입의 메소드를 가리킴)
* 단, 실질적으로 참조하는 자식클래스에 해당메소드가 오버라이딩 돼있다면
* 프로그램 실행시 동적으로 그 자식클래스의 오버라이딩 된 메소드를 찾아서 실행
*
*/
System.out.println("==================================");
for(int i = 0; i < arr.length; i++) {
arr[i].print();
}
}
}
실행 결과
제대로 다형성 사용 전 후 비교해보자
공통된 Class
Electronic Class
package com.kh.chap01_poly.part02_electronic.model.vo;
public class Electronic {
private String brand;
private String name;
private int price;
public Electronic() {
}
public Electronic(String brand, String name, int price) {
this.brand = brand;
this.name = name;
this.price = price;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public String toString() {
return "brand : " + brand + ", name : " + name + ", price : " + price;
}
}
Desktop Class
package com.kh.chap01_poly.part02_electronic.model.vo;
public class Desktop extends Electronic {
public static final String CPU = "intel";
private String graphic;
public Desktop() {
}
public Desktop(String brand, String name, int price, String graphic) {
super(brand, name, price);
this.graphic = graphic;
}
public String getGraphic() {
return graphic;
}
public void setGraphic(String graphic) {
this.graphic = graphic;
}
@Override
public String toString() {
return super.toString() + ", graphic : " + graphic;
}
}
NoteBook Class
package com.kh.chap01_poly.part02_electronic.model.vo;
public class NoteBook extends Electronic {
private int usbPort;
public NoteBook() {
}
public NoteBook(String brand, String name, int price, int usbPort) {
super(brand, name, price);
this.usbPort = usbPort;
}
public int getUsbPort() {
return usbPort;
}
public void setUsbPort(int usbPort) {
this.usbPort = usbPort;
}
@Override
public String toString() {
return super.toString() + ", usbPort : " + usbPort;
}
}
Tablet Class
package com.kh.chap01_poly.part02_electronic.model.vo;
public class Tablet extends Electronic {
private boolean penFlag;
public Tablet() {
}
public Tablet(String brand, String name, int price, boolean penFlag) {
super(brand, name, price);
this.penFlag = penFlag;
}
public boolean isPenFlag() {
return penFlag;
}
public void setPenFlag(boolean penFlag) {
this.penFlag = penFlag;
}
@Override
public String toString() {
return super.toString() + ", penFlag : " + penFlag;
}
}
다형성 적용 전
ElectronicShop1 Class
package com.kh.chap01_poly.part02_electronic.controller;
import com.kh.chap01_poly.part02_electronic.model.vo.Desktop;
import com.kh.chap01_poly.part02_electronic.model.vo.NoteBook;
import com.kh.chap01_poly.part02_electronic.model.vo.Tablet;
// 다형성 적용 안했을 때 !!!
public class ElectronicShop1 {
// 용산 전자상가에 있는 가게
// private int price; // 기본 자료형
// 3개 제품을 마련할 자리부터 만들자
private Desktop desk; // 내가 만든 자료형
private NoteBook note; // 객체 생성은 안되고 텅 비어있음
private Tablet tab;
// 이건 실제로 객체 생성 된 것은 아님
// 필드가 메모리상에 확보되는 순간 => 객체가 생성되는 순간!!!
public void insert(Desktop d) { // Desktop d = new Desktop(~~~);
desk = d;
}
public void insert(NoteBook n) { // Notebook n = new NoteBook(~~~);
note = n;
}
public void insert(Tablet t) { // Tablet t = new Tablet(~~~);
tab = t;
}
// 동일한 클래스에 동일한 메소드 명으로 여러개 만들어져있는 것 => 오버로딩
// 단, 매개변수 명은 달라야한다!!
// 오버로딩이 적용 돼 있음
// 조회
// 모르겠으면 일단은 반환병을 void 가자 => 나중에 수정 가능!
public Desktop selectDesktop() {
return desk;
}
public NoteBook selectNoteBook() {
return note;
}
public Tablet selectTablet() {
return tab;
}
// insert 처럼 select도 같은 이름으로 하면 안되는 이유는?
// 매개변수가 없기 때문에 불가능함!! => 오류남
// 현재는 메소드가 총 6개 !!
// 다형성 적용하면? 2개~3개로 줄이기 가능!!
}
실행 Class
package com.kh.chap01_poly.part02_electronic.run;
import com.kh.chap01_poly.part02_electronic.controller.ElectronicShop1;
import com.kh.chap01_poly.part02_electronic.model.vo.Desktop;
import com.kh.chap01_poly.part02_electronic.model.vo.NoteBook;
import com.kh.chap01_poly.part02_electronic.model.vo.Tablet;
public class Run {
public static void main(String[] args) {
// 실행용 클래스는 납품업체라 생각!
// 1. 다형성 적용 안했을 경우(ElectronicShop1)
ElectronicShop1 es = new ElectronicShop1();
// es에서 마련해놓은 desk, note, tab이라는 필드에
// 객체를 생성해서 넣어주기 위해서 우선 es 생성!!
// 메소드를 써서 생성하겠음
// 먼저 필요한 메소드를 생각해보자
// 아직은 없는 메소드
// 추가용 메소드 => insert
// 데스크탑 이라는 객체를 생성해서 넘기는 메소드
es.insert(new Desktop("samsung", "데땁", 1200000, "gtx1070"));
// 노트북이라는 객체를 생성해서 넘기는 메소드
es.insert(new NoteBook("LG", "그램", 2000000, 4));
// 태블릿 이라는 객체를 생성해서 넘기는 메소드
es.insert(new Tablet("Apple", "아이패드", 800000, false));
// 가게에 해당 객체들이 잘 생성돼있는지를 확인해보자
// 조회용 메소드 => select();
// String desk = es.selectDesktop();
Desktop d = es.selectDesktop();
NoteBook n = es.selectNoteBook();
Tablet t = es.selectTablet();
System.out.println(d);
System.out.println(n);
System.out.println(t);
}
}
실행 결과
다형성 적용 후
ElectronicShop2 Class
package com.kh.chap01_poly.part02_electronic.controller;
import com.kh.chap01_poly.part02_electronic.model.vo.Electronic;
// 다형성 개념을 적용했을때 !!
public class ElectronicShop2 {
// 자리를 마련하자
// 다형성 : 부모타입 레퍼런스 하나로 다양한 자식객체를 담을 수 있음!!
// 부모 : Electonic
// 자식 : Desktop, noteBook, Tablet
// Parrent[] arr = new Parent[4];
// Parent 타입
// arr[0] = new Child1(1,2,4)
// arr[1] = new child2(1,2,4)
// 부모타입 배열로 모든 자식 객체 다루기
private Electronic[] elec = new Electronic[3];
private int count = 0;
// inset 함수 구현
// 매개변수 => Desktop, NoteBook, Tablet 전부 다 받을 수 있는 부모 Electronic 으로 생성
public void insert(Electronic any) {
elec[count++] = any;
}
// 맨처음 => count : 0 이었을 경우
// any = new Desktop(~~~)
// elec[0] = new Desktop(~~~); 이거 실행하고 count 1
// 그다음 => count : 1 이었을 경우
// any = new NoteBook(~~~)
// elec[1] = new NoteBook(~~~); 이거싱행하고 count 2
// 그다음 => count : 2 이었을 경우
// any = new Tablet(~~~);
// elec[2] = new Tablet(~~~); 이거 실행하고 count 3
public Electronic select(int index) {
// Desktop => elec[0];
// NoteBook => elec[1];
// Tablet => elec[2];
return elec[index];
}
}
실행 Class
package com.kh.chap01_poly.part02_electronic.run;
import com.kh.chap01_poly.part02_electronic.controller.ElectronicShop2;
import com.kh.chap01_poly.part02_electronic.model.vo.Desktop;
import com.kh.chap01_poly.part02_electronic.model.vo.Electronic;
import com.kh.chap01_poly.part02_electronic.model.vo.NoteBook;
import com.kh.chap01_poly.part02_electronic.model.vo.Tablet;
public class Run {
public static void main(String[] args) {
// 실행용 클래스는 납품업체라 생각!
// 2. 다형성 적용 했을 경우 (ElectronicShop2)
ElectronicShop2 es = new ElectronicShop2();
// 추가용 메소드 => insert
es.insert(new Desktop("samsung", "데땁", 1200000, "gtx1070"));
es.insert(new NoteBook("LG", "그램", 2000000, 4));
es.insert(new Tablet("Apple", "아이패드", 800000, false));
// 조회용 메소드 => select
// 이번에는 이름을 하나로 !!
// es.select(인덱스값);
// Desktop d = es.select(0);
// 실제로 들어있는 값은 Desktop 객체가 맞으나 반환형이 Electronic 이라서 맞춰줘야함!! => 강제형변환
/*
Desktop d = (Desktop)es.select(0);
NoteBook n = (NoteBook)es.select(1);
Tablet t = (Tablet)es.select(2);
*/
Electronic d = es.select(0);
Electronic n = es.select(1);
Electronic t = es.select(2);
// 이런걸 다운캐스팅 이라고 한다!!!
// 위아래 방법 둘다 가능
// 그럼 toString() 할 때 부모에 있는 toString을 불러오지 않을까?
// 아님 => 왜?? 동적바인딩 !!
System.out.println(d);
System.out.println(n);
System.out.println(t);
}
}
실행 결과
다형성을 사용하는 이유
1. 부모타입 배열로 다양한 자식객체들을 받을 수 있음
2. 메소드 정의시 매개변수로 다형성을 적용하게 되면 메소드 개수가 확 줄어든다!!
3. 필수로 써야하는 곳이 있음 => 추상클래스(인터페이스)