210715 목
다형성
: 부모 타입의 레퍼런스로 여러 가지 종류의 자식 타입을 다룰 수 있는 기술
*레퍼런스
: 주소값을 갖는 변수
1> 부모 - Electronic
package com.kh.chap1_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) {
super();
this.brand = brand;
this.name = name;
this.price = price;
}
public String getBrand() {
return brand;
}
public String getName() {
return name;
}
public int getPrice() {
return price;
}
public void setBrand(String brand) {
this.brand = brand;
}
public void setName(String name) {
this.name = name;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public String toString() {
return "brand : " + brand + ", name : " + name + ", price : " + price;
}
}
2-1> 자식 - Desktop
package com.kh.chap1_poly.part02_electronic.model.vo;
public class Desktop extends Electronic{
private String graphic;
public Desktop() {}
public Desktop(String brand, String name, int price,
String grapic) {
super(brand, name, price);
this.graphic = grapic;
}
public String getGrapic() {
return graphic;
}
public void setGrapic(String grapic) {
this.graphic = grapic;
}
@Override
public String toString() {
return super.toString() + ", graphic : " + graphic;
}
}
2-2> 자식 - NoteBook
package com.kh.chap1_poly.part02_electronic.model.vo;
public class NoteBook extends Electronic{
private int usePort;
public NoteBook() {}
public NoteBook(String brand, String name, int price,
int usePort) {
super(brand, name, price);
this.usePort = usePort;
}
public int getUsePort() {
return usePort;
}
public void setUsePort(int usePort) {
this.usePort = usePort;
}
@Override
public String toString() {
return super.toString() + ", usePort : " + usePort;
}
}
2-3> 자식 - Tablet
package com.kh.chap1_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;
}
}
<실행>
package com.kh.chap1_poly.part02_electronic.run;
import com.kh.chap1_poly.part02_electronic.controller.ElectronicController;
import com.kh.chap1_poly.part02_electronic.model.vo.Desktop;
import com.kh.chap1_poly.part02_electronic.model.vo.Electronic;
import com.kh.chap1_poly.part02_electronic.model.vo.NoteBook;
import com.kh.chap1_poly.part02_electronic.model.vo.Tablet;
public class Run {
public static void main(String[] args) {
ElectronicController ec = new ElectronicController();
//1. 다형성 적용 안했을 경우
//물건 납품(insert)
/*ec.insert(new Desktop("Samsung", "데탑", 120000, "Geforce 1070"));
ec.insert(new NoteBook("LG", "그램", 2000000, 3));
ec.insert(new Tablet("apple", "아이패드", 800000, false));
//납품 된 물건 조회(select...)
Desktop d = ec.selectDesktop();
NoteBook n = ec.selectNoteBook();
Tablet t = ec.selectTablet();
//결과 출력
System.out.println(d);
System.out.println(n);
System.out.println(t);
<답>
brand : Samsung, name : 데탑, price : 120000, graphic : Geforce 1070
brand : LG, name : 그램, price : 2000000, usePort : 3
brand : apple, name : 아이패드, price : 800000, penFlag : false
*/
//2. 다형성 적용 했을 경우
ec.insert(new Desktop("Samsung", "데탑", 120000, "Geforce 1070"));
ec.insert(new NoteBook("LG", "그램", 2000000, 3));
ec.insert(new Tablet("apple", "아이패드", 800000, false));
//납품 된 물건 조회(select)
Electronic[] elec = ec.select();
//출력 for each문 사용
for(Electronic e :elec) {
System.out.println(e/*.toString()*/);
//e.toString 호출
//레퍼런스 타입이 Electronic(부모)이므로
//Electronic의 toString 메소드로 정적 바인딩 되어 있으나
//실제 실행시에는 해당 인스턴스(Desktop, NoteBook, Tablet)에
//오버라이딩 된 toSring 메소드를 실행함 => "동적 바인딩"
/*
* brand : Samsung, name : 데탑, price : 120000, graphic : Geforce 1070
brand : LG, name : 그램, price : 2000000, usePort : 3
brand : apple, name : 아이패드, price : 800000, penFlag : false
*/
//instacneof 연산자로 분류해서 다운 캐스팅한 후
//자식 객체가 가지는 기능 호출하기
if(e instanceof Desktop) {
System.out.println("Desktop : " + ((Desktop)e).getGrapic());
}else if(e instanceof NoteBook) {
System.out.println("NoteBook : " + ((NoteBook)e).getUsePort());
}else if(e instanceof Tablet) {
System.out.println("Tablet : " + ((Tablet)e).isPenFlag());
}
/*
* brand : Samsung, name : 데탑, price : 120000, graphic : Geforce 1070
Desktop : Geforce 1070
brand : LG, name : 그램, price : 2000000, usePort : 3
NoteBook : 3
brand : apple, name : 아이패드, price : 800000, penFlag : false
Tablet : false
*/
//Q. 다형성을 사용하는 이유?
//1. 부모타입의 객체 배열로 다양한 자식 타입을 다룰 수있다
//2. 매개변수에 다형성을 적용하는 경우 메소드를 줄일수 있다
}
}
}
1) 추상(abstract)
(1) 종류
1> 추상 클래스(abstract class) = 미완성이라고 이해하면 쉬움
: 몸체 없는 메소드를 포함한 클래스
-추상 클래스의 경우 클래스 선언부에 abstract 키워드 사용
형식)
[접근제한자] abstract class 클래스명 {}
2> 추상 메소드(abstract method)
: 몸체 없는 메소드
-추상 메소드의 선언부에 abstract 키워드 사용
-상속 시 반드시 구현해야 하는, 오버라이딩이 강제화되는 메소드
-내 클래스 안에 추상 메소드가 있다면 아직 미완성이라고 생각해야 한다.
형식)
[접근제한자] abstract 반환형 메소드명(자료형 변수명);
(2) 특징
1> 미완성 클래스(abstract 키워드 사용)
자체적으로 객체 생성 불가 → 반드시 상속하여 객체 생성
2> abstract 메소드가 포함된 클래스는 반드시 abstract 클래스
abstract 메소드가 없어도 abstract 클래스 선언 가능
3> 클래스 내에 일반 변수, 메소드 포함 가능
4> 객체 생성은 안되지만 참조형 변수 타입으로는 사용 가능
(3) abstract을 만드는 이유
: 규칙을 세우려는 의도
1> 부모 - Sprots
package com.kh.chap1_poly.chap02.abstractAndInterface.part01_abstract.model.vo;
public abstract class Sports {
//어떤 스포츠든 사람이 참여하므로
//참여하는 인원 수에 대한 일반 필드와 메소드 정의
private int people;
public Sports() {}
public Sports(int people) {
super();
this.people = people;
}
public int getPeople() {
return people;
}
public void setPeople(int people) {
this.people = people;
}
@Override
public String toString() {
return "Sports [people=" + people + "]";
}
//어떤 스포츠이든 지켜야할 룰은 있지만 스포츠마다 룰은 다르다
//=> 각 스포츠마다 그에 따른 룰을 반드시 정의해야 함(==강제 오버라이딩)
//=> 관련 메소드를 추상 메소드로 정의하자!
public abstract void rule();
//추상 메소드 : 미완성 된 메소드로 내용 구현이 되어있지 않은 메소드
//{} => 몸통부가 아직 구현이 되어 있지 않은 상태
//추상 메소드가 있다는 것은 클래스 또한 미완성이라는 의미이므로 => 반드시 추상 클래스로 선언
}
2-1> 자식 - Basketball
package com.kh.chap1_poly.chap02.abstractAndInterface.part01_abstract.model.vo;
public class Basketball extends Sports {
@Override
public void rule() {
// 상속 받는 순간 부모 클래스의 추상 메소드를 강제 오버라이딩 하게 함
System.out.println("공을 던져 링에 넣어야 한다.");
}
}
2-2> 자식 - Football
package com.kh.chap1_poly.chap02.abstractAndInterface.part01_abstract.model.vo;
public class Football extends Sports{
@Override
public void rule() {
System.out.println("손이 아닌 발로 공을 차야 한다.");
}
}
<실행>
package com.kh.chap1_poly.chap02.abstractAndInterface.part01_abstract.run;
import com.kh.chap1_poly.chap02.abstractAndInterface.part01_abstract.model.vo.Basketball;
import com.kh.chap1_poly.chap02.abstractAndInterface.part01_abstract.model.vo.Football;
import com.kh.chap1_poly.chap02.abstractAndInterface.part01_abstract.model.vo.Sports;
public class Run {
public static void main(String[] args) {
//추상 클래스로는 객체 생성 불가(미완성 클래스이므로)
//Sports s = new Sports();
Sports s;//객체 생성은 불가능 하지만, 레퍼런스로는 사용 가능하다
s = new Football(); // 다형성 적용해서 자식 객체 참조 가능
//Sports 타입의 객체 배열 만들기
Sports[] arr = new Sports[2];
arr[0] = new Basketball();
arr[1] = new Football();
//메소드 호출하기
for(int i = 0 ; i < arr.length ; i++) {
//"동적 바인딩"을 통해 자식 클래스에 오버라이딩 된 메소드 실행
arr[i].rule();
/*
* 공을 던져 링에 넣어야 한다.
손이 아닌 발로 공을 차야 한다.
*/
}
for(Sports sp : arr) {
sp.rule();
/*
* 공을 던져 링에 넣어야 한다.
손이 아닌 발로 공을 차야 한다.
*/
}
/*
* 추상 메소드가 있는 클래스를 상속 받는 경우 "강제 오버라이딩"
* => 메소드 사용에 통일성 확보 목적
* => 표준화 된 틀을 제공할 목적으로 사용
* => 상속보다 강력해진 규약의 개념
*
* 하지만 추상 메소드가 없어도 예약에를 붙여서 추상클래스로 만들수 있당께
* => 해당 클래스를 상속한 클래스만 객체를 생성하고 싶다는 의미
* => 객체 생성 방지 기능
*/
}
}
답 공을 던져 링에 넣어야 한다. 손이 아닌 발로 공을 차야 한다. |
2) 인터페이스
-상수형 필드와 추상 메소드만을 작성할 수 있는 추상 클래스의 변형체
-메소드의 통일성을 부여하기 위해 추상 메소드만 따로 모아놓은 것으로 상속 시 인터페이스 내에 정의된 모든 추상메소드 구현해야 함
형식)
[접근제한자] interface 인터페이스명 {}
public static final 자료형 변수명 = 초기값;
*상수도 멤버로 포함할 수 있음
[public abstract] 반환자료형 메소드명([자료형 매개변수]);
*추상 메소드만 선언 가능
**public abstract가 생략될 수 있다
***But, 오버라이딩 시 반드시 public 표기해야 함
(1) 특징
1> 모든 인터페이스의 메소드는 묵시적으로 public이고 abstract
2> 변수는 묵시적으로 public static final
따라서 인터페이스 변수의 값 변경 시도 시 컴파일 시 에러 발생
3> 객체 생성은 안되나 참조형 변수로는 가능
*묵시적 <-> 명시적
: 안써도 된다는 뜻
(2) 장점
-상위 타입 역할로 다형성을 지원하여 연결
-해당 객체가 다양한 기능 제공 시에도 인터페이스에 해당하는 기능만을 사용하게 제한 가능 공통 기능 상의 일관성 제공
-공동 작업을 위한 인터페이스 제공
1> 부모 - Phone(전화 기능)
package com.kh.chap1_poly.chap02.abstractAndInterface.part02_interface.model.vo;
public interface Phone {
//추상 클래스 => 일반 멤버(필드 + 메소드) + 추상 메소드,
//인터페이스 => 오로지 상수 필드 + 추상 메소드
//인터페이스에서의 필드는 무조건 상수 필드이다
/*public static final*/ int NUM = 10;//이것은 상수필드다
//인터페이스에서의 메소드는 무조건 추상 메소드
//키워드를 생략해도 컴파일 시 자동으로 생략 된 키워드를 추가한다
/*public abstract*/ void makeaaCall(); //전화 거는 기능
/*public abstract*/ void takeaCall(); //전화 받는 기능
}
2> 부모 - Camera(촬영기능)
package com.kh.chap1_poly.chap02.abstractAndInterface.part02_interface.model.vo;
public interface Camera {
void picture(); // 촬영 방식
}
3> 자식 - Cellphone(전화기능, 촬영기능 상속 + 충전 기능)
package com.kh.chap1_poly.chap02.abstractAndInterface.part02_interface.model.vo;
/*
* 인터페이스는 다중 상속이 가능하다
* => 단일 상속에 대한 제한점 극복 가능
*
* 클래스 간 : 클래스 extends 클래스
* 클래스 인터페이스간 : 클래스 implements 인터페이스, 인터페이스...
* 인터페이스간 : 인터페이스 extends 인터페이스, 인터페이스...
*
*
* 상속과 구현 둘 다 가능 : 클래스 extends 클래스 implements 인터페이스, 인터페이스..
*
*/
public interface CellPhone extends Phone, Camera{
//전화 걸기, 전화 받기, 촬영 방식을 상속 받고
//충전 방식 추가
void charge();
}
4> 부모 - TouchDisplay(터치 기능)
package com.kh.chap1_poly.chap02.abstractAndInterface.part02_interface.model.vo;
public interface TouchDisplay {
//터치 방식
void touch();
}
5> 자식 - SmartPhone((전화기능, 촬영기능, 충전 기능 상속 & 터치 기능 상속) + 메이커 추가)
package com.kh.chap1_poly.chap02.abstractAndInterface.part02_interface.model.vo;
public abstract class SmartPhone implements CellPhone, TouchDisplay{
//메이커 출력
public abstract void printMaker();
}
6-1> 자식1 - GalaxyS10(5번 기능 상속)
package com.kh.chap1_poly.chap02.abstractAndInterface.part02_interface.model.vo;
public class GalaxyS10 extends SmartPhone {
@Override
public void charge() {
System.out.println("고속 충전 , 고속 무선 충전");
}
@Override
public void makeaaCall() {
System.out.println("번호를 누르고 통화 버튼을 누름");
}
@Override
public void takeaCall() {
System.out.println("수신 버튼을 누름");
}
@Override
public void picture() {
System.out.println("1300만 듀얼 카메라");
}
@Override
public void touch() {
System.out.println("정전식, 와콤펜 충전");
}
@Override
public void printMaker() {
System.out.println("Galaxy 10은 삼성꺼다구");
}
}
6-2> 자식2 - GalaxyS10(5번 기능 상속)
package com.kh.chap1_poly.chap02.abstractAndInterface.part02_interface.model.vo;
public class V50 extends SmartPhone{
@Override
public void charge() {
// TODO Auto-generated method stub
System.out.println("고속 충전");
}
@Override
public void makeaaCall() {
// TODO Auto-generated method stub
System.out.println("번호를 누르고 통화 버튼을 누름");
}
@Override
public void takeaCall() {
// TODO Auto-generated method stub
System.out.println("수신 버튼을 누름");
}
@Override
public void picture() {
// TODO Auto-generated method stub
System.out.println("1200, 1600만 화소 트리플 카메라");
}
@Override
public void touch() {
// TODO Auto-generated method stub
System.out.println("정전식");
}
@Override
public void printMaker() {
// TODO Auto-generated method stub
System.out.println("V50은 LG꺼");
}
}
<실행>
package com.kh.chap1_poly.chap02.abstractAndInterface.part02_interface.run;
//SmartPhone에는 통화, 카메라, 터치 기능이 있음
//해당 기능을 각각 interface로 구현하여 SmartPhone 클래스에서 다중 상속 받은 뒤
//추상 클래스인 SmartPhone을 상속 받아서 GalaxyS10과 V50을 구현 헤헤 구려
import com.kh.chap1_poly.chap02.abstractAndInterface.part02_interface.model.vo.Camera;
import com.kh.chap1_poly.chap02.abstractAndInterface.part02_interface.model.vo.GalaxyS10;
import com.kh.chap1_poly.chap02.abstractAndInterface.part02_interface.model.vo.Phone;
import com.kh.chap1_poly.chap02.abstractAndInterface.part02_interface.model.vo.SmartPhone;
import com.kh.chap1_poly.chap02.abstractAndInterface.part02_interface.model.vo.TouchDisplay;
import com.kh.chap1_poly.chap02.abstractAndInterface.part02_interface.model.vo.V50;
//1. Phone(interface) : 통화 기능
//2. Camera(interface) : 촬영 기능
//3. CellPhone(interface) : 휴대 전화(Phone, Camera 상속)
//4. TouchDisplay(interface) : 터치 기능
//5. SmartPhone(abstract class) : 스마트폰(CellPhone, TouchDisplay 상속)
//6. GalaxyS10(class) : SmartPhone을 상속 받은 자식 클래스
//7. V50(class) : SmartPhone을 상속 받은 자식 클래스
public class Run {
public static void main(String[] args) {
//Phone 인터페이스의 상수 필드에 접근 가능ㅋ
System.out.println(Phone.NUM);
//인터페이스는 객체 생성 불가
//Phone p = new Phone();
//But, 부모 타입의 레퍼런스로는 사용이 가능하다
Phone p = new GalaxyS10();
//객체 배열을 만들어 각각의 인덱스에 GalaxyS10, V50객체 저장
SmartPhone[] phone = new SmartPhone[2];
phone[0] = new GalaxyS10();
phone[1] = new V50();
//반복문을 통해 각 객체의 모든 메소드를 호출하여 정보 출력
for(int i = 0 ; i < phone.length ; i++) {
//실행 시 동적 바인딩이 적용 되어
//해당 객체에서 오버라이딩 된 메소드가 호출 됨
phone[i].printMaker();
phone[i].makeaaCall();
phone[i].takeaCall();
phone[i].picture();
phone[i].touch();
phone[i].charge();
System.out.println();
if(phone[i] instanceof Phone) {
System.out.println("Phone");
}
if(phone[i] instanceof Camera) {
System.out.println("Camera");
}
if(phone[i] instanceof TouchDisplay) {
System.out.println("TouchDisplay");
}
}
/*
* Q. 인터페이스를 사용하는 이유가 뭘까용?
* A. 통화, 카메라, 터치 별로 갖춰야 할 기능을 인터페이스의 메소드로 구현해두면
* 그 인터페이스를 상속받은 모든 클래스들은 반드시 그 메소드를 구현해야 함
* => 공통 기능상의 일관성 제공(표준화)
*
* 통화, 카메라, 터치 등 여러 인터페이스를 조합하여 기능을 만들 수 있음
* => 단일 상속에 대한 제한점 극복 가능
*
* 상위 타입 역할로 다형성 지원하여 연결
* => 부모 타입 레퍼런스로 하위 타입 엮어서 사용
* => 메소드의 매개변수, 리턴 타입으로도 활용하여 메소드 개수 줄일 수 있음
*
* 공동 작업을 위한 인터페이스 제공(개발 시간 단축)
* => 메소드 호출 시 선언부만 알면 되기 때문에 서로 영향을 주고 받는 공동 작업 시
* 본인의 영역 개발 가능
*
*/
}
}
답 Galaxy 10은 삼성꺼 번호를 누르고 통화 버튼을 누름 수신 버튼을 누름 1300만 듀얼 카메라 정전식, 와콤펜 충전 고속 충전 , 고속 무선 충전 Phone Camera TouchDisplay V50은 LG꺼 번호를 누르고 통화 버튼을 누름 수신 버튼을 누름 1200, 1600만 화소 트리플 카메라 정전식 고속 충전 |
instanceof 답 Phone Camera TouchDisplay |
3) 추상클래스 vs 인터페이스
<요약>
1) OOP란 소프트웨어를 어떻게 만들거냐 하는 방법론이다
2) 그렇다면 소프트웨어 방법론은 왜 있는가?
: 소프트웨어를 경제성있게 만들기 위해
3) 캡슐화, 상속, 다형성 등은 소프트웨어를 조금더 경제성있게 만들 수 있게끔 하는 구조의 한 유형이다. 즉, OOP를 실행한다.
개념정리 열심히 하기!
=> 혼공자, 자바의 정석 구매 완,
'웹개발 수업 > JAVA' 카테고리의 다른 글
[Day +19]API / String클래스, Wrapper클래스, 날짜 클래스, Format 클래스, 예외 처리 (0) | 2021.07.20 |
---|---|
[Day+18]객체 총정리, API (0) | 2021.07.16 |
[Day +16] 자바 1차 시험 (0) | 2021.07.14 |
[Day +15]상속, 다형성 / 오버라이딩, toString, equals, hashcode, instanceof, 캐스팅, 바인딩 (0) | 2021.07.13 |
[Day +14]상속 / 오버라이딩, 오버로딩, for each문 (0) | 2021.07.12 |