클라우드 융합 Full-stack 웹 개발자 양성과정/Java

프로그래밍 언어응용-다형성

thesunset 2022. 9. 25. 16:14

다형성은 객체지향프로그래밍의 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]);
}

> 다형성을 사용하는 이유

* 부모타입의 자료형으로 다양한 자식객체들을 모두 다룰 수 있음(표면적이유)

*

* 메소드의 개수가 줄어듬 => 코드가 짧아짐

* 부모타입의 객체배열로 자식객체들 사용가능(반복문 사용가능)

* 소스코드가 줄어드니까 효율성이 높아진다.

}

}