다형성은 객체지향프로그래밍의 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("부모메소드입니다.");
}
public void print() {
System.out.println("부모메소드입니다.");
}
}
# Child1
package com.kh.chap01_poly.part01_basic.model.vo;
public class Child1 extends Parent{
//필드부
//int x, int y
private int z;
//기본생성자, 모든 필드가 매개변수인 생성자, getter/setter, printChild1
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
package com.kh.chap01_poly.part01_basic.model.vo;
public class Child2 extends Parent {
//필드부
//int x, int y
private int n;
public Child2() {
super();
}
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("두번째 자식메소드입니다.");
}
}
# Run
package com.kh.chap01_poly.part01_basic.run;
import com.kh.chap01_poly.part01_basic.model.vo.*;
public class Run {
public static void main(String[] args) {
> 상속관계에서 자식클래스에게 물려주는 것은?
1. ☆☆☆☆☆☆☆☆타입(자료형)☆☆☆☆☆☆☆☆=>가장중요
2. 필드, 메소드
> 대입연산자의 특징
1. 우항의 값을 좌항에 대입한다.
2. ★★★★★★★★양변의 자료형이 같아야한다.
- 부모클래스는 자식클래스 o 자식클래스는 자식클래스만
1. 부모타입 자료형으로 부모 객체를 다루는 경우
Parent p1 = new Parent();
p1.printParent();
//p1.printChild1(); => 자식클래스이기때문에 부모가 접근 불가능
//p1으로는 Parent에만 접근 가능
2. 자식타입 자료형으로 자식 객체를 다루는 경우
Child1 c1 = new Child1();
c1.printChild1();
c1.printParent(); //자식클래스는 부모, 자식 모두 가능
//c1으로는 Parent, Child1에 둘 다 접근 가능
3. 부모타입 자료형으로 자식객체를 다루는 경우 =>다형성이 적용된 개념
Parent p2 = /*(Parent)*/new Child1(); //자식객체의 주소값 대입
p2.printParent();
//p2.printChild(); -> parent로 형변환했기때문에 사용불가능
//p2로는 parent만 접근이 가능
//양쪽의 자료형이 다름에도 불구하고 오류가 나지 않음.
//왜? Parent형으로 자동형변환되었기때문에
((Child1)p2).printChild1();//우선순위가 .(참조연산자)가 빠르기 때문에 괄호를 붙여줘야함.
//Child1 c2 = new Parent();//자식의 타입은 물려받지 않기때문에 불가능
//Child1 c2 = (Child1)new Parent();
//c2.printChild(); => 강제형변환하더라도 자식의 영역이 확장되어 더 크기 때문에 조회 안됨

> 클래스 형변환
: "상속구조"일 경우에만 클래스 간 형변환이 가능함
1.Upcasting
* 자식타입 => 부모타입
* 생략이 s가능.
* ex) 자식.부모메소드();
2.Downcasting
* 부모타입 => 자식타입
* 생략이 불가능
* ex) ((자식)부모).자식메소드();
> 다형성을 사용하는 이유?
- 배열을 써볼 경우,
- 배열의 특징 == 한 종류의 자료형인 값들을 묶어서 관리가 가능, 반복문 사용가능
//다형성을 배우기 전
//Child1 객체 2개 Child2 객체 2개를 관리해야 함
Child1[] arr1 = new Child1[2];
arr1[0] = new Child1(1,2,3);
arr1[1] = new Child1(4,5,6);
Child2[] arr2 = new Child2[2];
arr2[0] = new Child2(2,3,4);
arr2[1] = new Child2(3,4,5);
System.out.println("---------다형성을 접목하기 전 배열---------");
arr1[0].printChild1();
arr1[1].printChild1();
arr2[0].printChild2();
arr2[0].printChild2();
System.out.println("---------다형성을 접목한 후 배열---------");
Parent[]arr = new Parent[4];
arr[0]=/*(Parent)*/new Child1(1,2,3); //부모클래스 배열에 자식클래스를 대입
arr[1]=/*(Parent)*/new Child2(2,3,4);
arr[2]=/*(Parent)*/new Child2(3,4,5);
arr[3]=/*(Parent)*/new Child1(4,5,6);
//printParent()메소드 호출
for(int i =0; i<arr.length; i++) {
arr[i].printParent();
}
//printChild1,2()메소드 호출은?
((Child1)arr[0]).printChild1();
((Child2)arr[1]).printChild2();
((Child2)arr[2]).printChild2();
((Child1)arr[3]).printChild1();
//Child1을 Child2로 강제형변환을 한다면?//자식끼리
//((Choild2)arr[0]).printChild2); //상속관계가 아닌 관계는 되지 않음.
//ClassCastException이 발생
//클래스 간에 형변환이 잘못되었을 경우 발생함
System.out.println("반복문+오버라이딩");
for(int i = 0; i<arr.length; i++) {
if(arr[i] instanceof Child1) {
((Child1)arr[i]).printChild1();
}else {
((Child2)arr[i]).printChild2();
}
}
}
}
> instanceof 연산자 => true / false
: 현재 래퍼런스가 실질적으로 어떤 클래스타입을 참조하는지 확인할 때 사용
//각 배열의 인덱스에 차근차근 접근
arr[i].print(); //실제론 parent가 아닌 child이기 때문에
//부모인덱스가 4번찍혀야함.
//정작결과는 오버라이딩된 print각각호출
* 동적 바인딩: 프로그램 실행 전 컴파일 되면서 정적바인딩(자료형의 메소드를 가리킴)
단, 실질적으로 참조하는 자식클래스에 해당 메소드가 오버라이딩 되어있다면
프로그램 실행 시 동적으로 그 자식클래스의 오버라이딩 된 메소드를 찾아서 실행
//우리가 앞으로 객체 배열을 쓸 때 오버라이딩을 이용하면 굳이 강제 형변환을 하지 않아도 됨.
# Electronic
package com.kh.chap01_poly.part02_electoric.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;
}
//int a = 0;
//sout(a.toString); -> x
//sout(e.toString); -> 객체일때만 자동으로 붙어 생략됨
@Override
public String toString() {
return "Electronic [brand=" + brand + ", name=" + name + ", price=" + price + "]";
}
}
# Tv
package com.kh.chap01_poly.part02_electoric.model.vo;
public class Tv extends Electronic {
private boolean blackWhite;
public Tv() {}
public Tv(String brand, String name, int price, boolean blackWhite) {
super(brand, name, price);
this.blackWhite = blackWhite;
}
public boolean isBlackWhite() { //boolean 은 is가 붙음
return blackWhite;
}
public void setBlackWhite(boolean blackWhite) {
this.blackWhite = blackWhite;
}
@Override
public String toString() {
return super.toString() + "Tv [blackWhite=" + blackWhite + "]";
}
}
# SmartPhone
package com.kh.chap01_poly.part02_electoric.model.vo;
public class SmartPhone extends Electronic {
private String phoneNumber;
public SmartPhone() {}
public SmartPhone(String brand, String name, int price, String phoneNumber) {
super(brand, name, price);
this.phoneNumber = phoneNumber;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
@Override
public String toString() {
return super.toString() + "SmartPhone [phoneNumber=" + phoneNumber + "]";
}
}
# AirPod
package com.kh.chap01_poly.part02_electoric.model.vo;
public class AirPod extends Electronic {
private boolean lose;
public AirPod() {}
public AirPod(String brand, String name, int price, boolean lose) {
super(brand, name, price);
this.lose = lose;
}
public boolean isLose() {
return lose;
}
public void setLose(boolean lose) {
this.lose = lose;
}
@Override
public String toString() {
return super.toString() + "AirPod [lose=" + lose + "]";
}
}
# ElectronicController
package com.kh.chap01_poly.part02_electoric.controller;
import com.kh.chap01_poly.part02_electoric.model.vo.*;
// 용산 전자상가에 새로 차린 가게: 다형성 적용 전
public class ElectronicController1 {
// 필드부
private Tv tv; // Run클래스의 new가 들어가있음
private SmartPhone sp;
private AirPod ap;
//생성자부 생략
//JVM이 기본생성자를 자동으로 생성해줌
// 메소드부
// 재고를 추가해주는 기능
// 같은 메소드명 => 오버로딩
// 매개변수의 자료형이나 개수나 순서가 달라야 한다.
//set역할을 하는 insert메소드
public void insert(Tv tv) {
this.tv = tv; //tv의 주소값을 tv필드에 대입
}
public void insert(SmartPhone sp) {
this.sp = sp;
}
public void insert(AirPod ap) {
this.ap = ap;
}
//get역할
//오버로딩불가(매개변수가 없기때문에)
public Tv selectTv() {
return tv;
}
public SmartPhone selectSmartPhone() {
return sp;
}
public AirPod selectAirPod() {
return ap;
}
}
# ElectronicController2
package com.kh.chap01_poly.part02_electoric.controller;
import com.kh.chap01_poly.part02_electoric.model.vo.Electronic;
//용산 전자상가에 새로 차린 가게(좋게차림) : 다형성 적용
public class ElectronicController2 {
//필드부
private Electronic[] elsc = new Electronic[3];
//메소드부
//재고추가 메소드
//객체 타입, 인덱스번호
public void insert(Electronic any, int i) {
elsc[i] = any;
}
//재고하나를 조회해주는 메소드
public Electronic select(int i) {
return elsc[i];
}
//재고를 모두 조회
public Electronic[] selectAll() {
return elsc;
}
}
# Run
package com.kh.chap01_poly.part02_electoric.run;
import com.kh.chap01_poly.part02_electoric.controller.ElectronicController2;
import com.kh.chap01_poly.part02_electoric.model.vo.*;
public class Run {
public static void main(String[] args) {
1. 다형성을 적용 안했을 경우(ElectronicController1)
- 객체를 생성: 재고를 담을 필드들이 메모리 영역에 올라감
ElectronicController1 ec1 = new ElectronicController1();
//재고추가
ec1.insert(new Tv("LG", "짱짱조은티비", 3000000, false)); //Tv tv = new Tv();
ec1.insert(new SmartPhone("베가", "LG", 20000, "010-1111-2222"));
ec1.insert(new AirPod("애플", "에어팟", 500000, false));
//가게에 있는 제품들을 조회
Tv tv = ec1.selectTv();//주소값을 돌려줌 재활용할 것이기에 변수에 담아주기(Tv형의)
SmartPhone sp = ec1.selectSmartPhone();
AirPod ap = ec1.selectAirPod();
System.out.println(tv);
System.out.println(sp);
System.out.println(ap);
2.다형성을 적용했을 경우(ElectronicController2)
- 객체생성
ElectronicController2 ec2 = new ElectronicController2();
ec2.insert(new Tv("LG", "짱짱조은티비", 3000000, false), 0); //electronic(부모)를 참조한 객체에 값 대입
ec2.insert(new SmartPhone("베가", "LG", 20000, "010-1111-2222"), 1);
ec2.insert(new AirPod("애플", "에어팟", 500000, false), 2);
//형변환
Tv tv = (Tv)ec2.select(0);
SmartPhone sp = (SmartPhone)ec2.select(1);
AirPod ap = (AirPod)ec2.select(2);
//재고조회
System.out.println(tv);
System.out.println(sp);
System.out.println(ap);
//재고 모두 조회
Electronic[] elec = ec2.selectAll();
System.out.println("----------모두조회----------");
for(int i = 0; i<elec.length; i++) {
System.out.println(elec[i]);
}

> 다형성을 사용하는 이유
* 부모타입의 자료형으로 다양한 자식객체들을 모두 다룰 수 있음(표면적이유)
*
* 메소드의 개수가 줄어듬 => 코드가 짧아짐
* 부모타입의 객체배열로 자식객체들 사용가능(반복문 사용가능)
* 소스코드가 줄어드니까 효율성이 높아진다.
}
}
'클라우드 융합 Full-stack 웹 개발자 양성과정 > Java' 카테고리의 다른 글
프로그래밍 언어응용 - API(String, Math, Wrapper), IO (1) | 2022.09.25 |
---|---|
프로그래밍 언어응용-추상화, 인터페이스, 예외처리 (1) | 2022.09.25 |
프로그래밍 언어응용-상속 (1) | 2022.09.25 |
프로그래밍 언어응용-객체5 (1) | 2022.09.25 |
프로그래밍 언어응용-객체3, 4 (0) | 2022.09.25 |