본문 바로가기

웹개발 수업/JAVA

[Day +17]다형성, 추상 클래스, 추상 메소드, 인터페이스

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를 실행한다.

 

 

개념정리 열심히 하기!

=> 혼공자, 자바의 정석 구매 완,