// 09.상속이란 - 1 & 10.상속이란 - 2
상속 & 다형성 > 유지 보수, 확장성 있는 시스템 설계 가능
추상 클래스, 인터페이스까지 이어지는 부분
| 클래스에서 상속의 의미
새로운 클래스를 정의할 때 이미 구현된 클래스를 상속(inheritance) 받아서
속성이나 기능이 확장되는 클래스를 구현함
상위 클래스 : A 클래스 | A 클래스가 B 클래스에게 상속한다 = B 클래스가 A 클래스를 상속받는다 |
하위 클래스 : B 클래스 |
상속하는 클래스 : 상위 클래스, parent class, base class, super class
상속 받는 클래스 : 하위 클래스, child class, derived class, subclass
| 상속을 사용하는 경우
상위 클래스는 하위 클래스 보다 일반적인 개념과 기능을 가짐
하위 클래스는 상위 클래스 보다 구체적인 개념과 기능을 가짐
포유류 |
⬆ |
사람 |
class Mammal{
}
class Human extends Mammal {
}
extends 뒤에는 단 하나의 class만 사용할 수 있음
자바는 single inheritance만을 지원함
| 코딩 하기 - 상속을 사용하여 고객관리 프로그램 구현하기
고객 등급에 따른 차별화된 서비스를 제공할 수 있다
고객의 등급에 따라 할인율, 적립금이 다르게 적용된다
구현해보자
| Customer 클래스
멤버 변수 | 설명 |
customerID | 고객 아이디 |
customerName | 고객 이름 |
customerGrade | 고객 등급 기본 생성자에서 지정되는 기본 등급은 SILVER |
bonusPoint | 고객의 보너스 포인트
|
bonusRatio | 보너스 포인트 적립 비율
|
/// Customer.java ///
package inheritance;
public class Customer{
protected int customerId;
protected String customerName;
protected String customerGrade;
int bonusPoint;
double bonusRatio;
public Customer(){
customerGrade = “SILVER”;
bonusRation = 0.01;
}
public int calcPrice(int price){
bonusPoint += price * bonusRation;
return price;
}
//?
public String showCustomerInfo(){
return customerName + “ 님의 등급은 ” + customerGrade + “ 이고, 적립한 포인트는 ” + bonusPoint + “ 입니다. ”
}
public int getCustomerId(){
return customerId;
}
public void setCustomerId(int customerId){
this.customerId = customerId;
}
public String getCustomerName(){
return customerName;
}
public void setCustomerName(String customerName){
this.customerName = customerName;
}
public String getCustomerGrade(){
return customerGrade;
}
public void setCustomerGrade(String customerGrade){
this.customerGrade = customerGrade;
}
}
—-----------—-----------—- 상속이란 - 2
| Customer를 상속받아 구현하는 VIPCustomer 클래스
VIPCustomer 클래스의 기능 : 단골고객으로 혜택이 더 다양해짐
제품 구매시 10% 할인
보너스 포인트 5% 적립
담당 상담원 배정
Customer 클래스와 유사하지만, 그보다 더 많은 속성과 기능이 필요
customerID customerName customerGrade bonusPoint bonusRatio |
Customer 클래스 |
⬆ | |
agentID salesRation |
VIPCustomer 클래스 |
| protected 예약어
외부 클래스에는 private으로 하위 클래스에서 public의 기능을 구현한 키워드
상위 클래스에 protected로 선언된 변수나 메서드는 다른 외부 클래스에서는
사용할 수 없지만 하위 클래스에서는 사용 가능
- private으로 access modifier이 설정이 되어있으면
상속 클래스라도 사용할 수 없음
> 고로 protected 사용
| 접근 제한자(access modifier)의 가시성
외부 클래스 | 하위 클래스 | 동일 패키지 | 내부 클래스 | |
public | O | O | O | O |
protected | X | O | O | O |
default (선언되지 않음) |
X | X | O | O |
private | X | X | X | O |
/// VIPCustomer.java ///
package inheritance;
public class VIPCustomer extends Customer{
double saleRatio;
private int agentId;
public VIPCustomer(){
customerGrade = “VIP”
bonusRatio = 0.05;
saleRatio = 0.1;
}
}
| 테스트 시나리오
일반 고객 1명과 VIP 고객 1명이 있다
일반 고객의 이름은 이순신, 아이디는 10010, 보너스 포인트는 1000점
VIP 고객의 이름은 김유신, 아이디는 10020, 보너스 포인트는 10000점
두 고객을 생성하고 이에 대한 고객 정보를 출력해라
/// CustomerTest.java ///
package inheritance;
public class CustomerTest{
public static void main(String[] args){
Customer customerLee = new Customer();
customerLee.setustomerName(“이순신");
customerLee.setCustomerId(10010);
customerLee.bonusPoint = 1000;
System.out.println(customerLee.showCustomerInfo());
VIPCustomer customerKim = new VIPCustomer();
customerKim.setCustomerName(“김유신");
customerKim.setCustomerId(10020);
customerKim.bonusPoint = 10000;
System.out.println(customerKim.showCustomerInfo());
}
}
—-----------—-----------—-----------—-----------—-----------—-----------—-----------—-----------
—-----------—-----------—-----------—-----------—-----------—-----------—-----------—-----------
// 11. 상속에서 클래스 생성과정과 형 변환
| 하위 클래스가 생성되는 과정
하위 클래스가 생성 될 때 상위 클래스가 먼저 생성 됨
상위 클래스의 생성자가 호출되고 하위 클래스의 생성자가 호출 됨
하위 클래스의 생성자에서는 무조건 상위 클래스의 생성자가 호출되어야 함
하위 클래스에서 상위 클래스의 생성자를 호출하는 코드가 없는 경우
컴파일러는 상위 클래스 기본 생성자를 호출하기 위한
super( )를 추가함
super( )
상위 클래스의 메모리 위치, 참조값을 가지고 있다
super( )을 사용시 상위 클래스의 디폴트(기본) 생성자가 호출된다
만약 상위 클래스의 기본생성자가 없는 경우 ( 매개변수가 있는 생성자만 존재 하는경우 )
하위 클래스는 명시적으로 상위 클래스의 생성자를 호출해야 함
| 상속에서의 메모리 상태
상위 클래스의 인스턴스가 만저 생성이 되고,
하위 클래스의 인스턴스가 생성 됨
< 힙 메모리 >
customerId |
customerName |
customerGrade |
bonusPoint |
bonusRatio |
agentId |
salesRatio |
- BLUE_Customer() 생성자 호출 > Customer 클래스의 멤버 변수가 메모리에 생성됨
- RED_VIPCustomer() 생성자 호출 > VIPCustomer 클래스의 멤버 변수가 메모리에 생성됨
| 상위 클래스로의 묵시적 형 변환(업캐스팅)
상위 클래스 형으로 변수를 선언하고 하위 클래스 인스턴스를 생성할 수 있다
하위 클래스는 상위 클래스의 타입을 내포하고 있으므로
상위 클래스로 묵시적 형변환이 가능함
상속 관계에서 모든 하위 클래스는 상위 클래스로 묵시적 형 변환이 됨
BUT 그 역은 성립하지 않음
선언된 클래스형 ( 상위 클래스형 ) |
생성된 인스턴스의 클래스형 ( 하위 클래스형 ) |
|||||
Customer vc = new VIPCustomer( ); |
| 형 변환에서의 메모리
Customer vc = newVIPCustomer(); 에서 vc가 가리키는 것은?
>
VIPCustomer() 생성자의 호출로 인스턴스는 모두 생성 되었지만
타입이 Customer 이므로 접근 할 수 있는 변수나 메서드는
Customer의 변수와 메서드임
| 클래스 계층구조가 여러 단계인 경우 ( = 가능 )
Ex) Human은 내부적으로 Primate와 Mammal의 자료형을 모두 내포하고 있음
포유류 (Mammal) | |
⬆ | |
호랑이 (Tiger) | 영장류 (Priamte) |
⬆ | |
인간 (Human) |
Primate aHuman =new Human();
Mammal mHuman = new Human();
/// CustomerTest2.java ///
package inheritance;
public class CustomerTest{
public static void main(String[] args){;
// 하위 클래스 생성자가 생성될 시
VIPCustomer customerKim = new VIPCustomer( 10020, “김유신” );
// customerKim.setCustomerName(“김유신");
// customerKim.setCustomerId(10020);
customerKim.bonusPoint = 10000;
System.out.println(customerKim.showCustomerInfo());
// 메서드 안에 로그를 남기면, 상위 클래스에서 기본 생성자가 생기는 것 확인 가능
// super( )이 생성이 됨, 프리컴파일 단계에서
}
}
/// Customer.java ///
package inheritance;
public class Customer{
protected int customerId;
protected String customerName;
protected String customerGrade;
int bonusPoint;
double bonusRatio;
public Customer(){
customerGrade = “SILVER”;
bonusRation = 0.01;
}
public Customer( int customerId, String customerName ){
this.customerId = customerId;
this.customerName = customerName;
customerGrade = “SILVER”;
bonusRatio = 0.01;
}
~~~~
}
/// VIPCustomer.java ///
package inheritance;
public class VIPCustomer extends Customer{
double saleRatio;
private int agentId;
// 기본생성자가 없고, 매개변수 있는 생성자가 있을 시에는
// 하위 클래스의 생성자에도 매개변수를 두고, super( int, String )으로 한다
// 허나 상위 클래스에 어떠한 생성자도 없을 경우
// 기본 생성자가 생성이 된다 ( 컴파일러가 제공함 )
public VIPCustomer( int customerId, String customerName ){
super( customerId,customerName );
customerGrade = “VIP”
bonusRatio = 0.05;
saleRatio = 0.1;
}
}
—-----------—-----------—-----------—-----------—-----------—-----------—-----------—-----------
—-----------—-----------—-----------—-----------—-----------—-----------—-----------—-----------
// 12. 메서드 오버라이딩
| 하위 클래스에서 메서드 재정의 하기
오버라이딩 ( overriding )
: 상위 클래스에 정의된 메서드의 구현 내용이 하위 클래스에서 구현할
내용과 맞지 않는 경우 하위 클래스에서 동일한 이름의 메서드를 재정의 할 수 있음
예제의 Customer 클래스의 calcPrice()와 VIPCustomer의 calcPrice() 구현 내용은
할인율과 보너스 포인트 적립 내용 부분의 구현이 다름
따라서 VIPCustomer 클래스는 calcPrice() 메서드를 재정의 해야 함
///
public VIPCustomer( int customerId, String customerName ){
super( customerId,customerName );
customerGrade = “VIP”
bonusRatio = 0.05;
saleRatio = 0.1;
}
@Override
public int calcPrice(int price){
bonusPoint = price*bonusRatio;
return price - ( int ) ( price*saleRatio )
}
| @override 애노테이션 (Annotation)
재정의된 메서드라는 의미로 선언부가 기존의 메서드와 다른 경우 에러 발생
애노테이션은 컴파일러에게 특정한 정보를 제공해주는 역할
애노테이션 | 설명 |
@Override | 재정의된 메서드라는 정보 제공 |
@Functionalinterface | 함수형 인터페이스라는 정보 제공 |
@Deprecated | 이후 버전에서 사용되지 않을 수 있는 변수, 메서드에 사용됨 |
@SuppressWarnings | 특정 경고가 나타나지 않도록 함 EX) @SuppressWarnings(“deprecation”)은 @Deprecated가 나타나지 않도록 함 |
| 형 변환과 오버라이딩 메서드 호출
Customer vc =new VIPCustomer();
vc.calcPrice(10000);
위 코드에서 calcPrice() 메서드는 어느 메서드가 호출될 거 인가?
자바에서는 항상 인스턴스 ( 여기서는 VIPCustomer )의 메서드가 호출됨
/// OverridingTest.java ///
package inheritance;
public class Overriding{
public static void main(String[] args){
Customer customerSeo = new VIPCustomer(10030, “서상원");
/// customerSeo는 VIP등급으로 지정됨 B/C VIPCustomer로 생성되니까
}
}
| 가상 메서드 ( virtual method )
메서드의 이름과 메서드 주소를 가진 가상 메서드 테이블에서
호출될 메서드의 주소를 참조함
Customer 클래스의 가상 메서드 테이블 | 메서드 영역 | ||
메서드 | 메서드 주소 | Customer 클래스 calcPrice() |
|
calcPrice(재정의됨) | 0xFF00FFAA | ||
showCustomerInfo(재정의X) | 0x112233AA | Customer 클래스 showCustomerInfo() |
|
VIPCustomer 클래스의 가상 메서드 테이블 | |||
메서드 | 메서드 주소 | VIPCustomer클래스 재정의된 calcPrice() |
|
calcPrice(재정의됨) | 0x00335577 | ||
showCustomerInfo(재정의X) | 0x112233AA | VIPCustomer 클래스 getAgentID() |
|
getAgentID (하위 클래스에서 추가된 메서드 ) |
0x8899BB33 |
메서드의 이름은 그 자체가 주소 고로 같은 이름 존재 X
BUT 메서드가 재정의 되는 경우는 이름이 같을 수도 있다.
이러할 경우에는 각각 맵핑되는 주소가 따로 있다.
또한 호출은 타입에 기반이 아닌 생성된 인스턴스에 기반해서 호출된다.
| 재정의된 메서드의 호출 과정
Customer vc = new VIPCustomer(); 일때
vc.calcPrice(); | ➡ calcPrice()재정의 안된 경우 호출 ➡ | Customer 클래스 calcPrice() |
➡ calcPrice()재정의된 경우 호출 ➡ | VIPCustomer 클래스 재정의한 calcPrice() |
—-----------—-----------—-----------—-----------—-----------—-----------—-----------—-----------
—-----------—-----------—-----------—-----------—-----------—-----------—-----------—-----------
// 13. 다형성 1 & 14. 다형성 2
| 다형성 ( polymorphism ) 이란?
하나의 코드가 여러 자료형으로 구현되어 실행되는 것
같은 코드에서 여러 실행 결과가 나옴
정보은닉, 상속과 더불어 객체지향 프로그래밍의 가장 큰 특징 중 하나
객체지향 프로그래밍의 유연성, 재활용성, 유지보수성에 기본이 되는 특징
/// AnimalTest.java ///
package polymorphism;
class Animal{
public void move(){
System.out.println(“동물이 움직입니다");
}
}
class Human extends Animal {
public void move(){
System.out.println(“사람이 두 발로 걷습니다");
}
public void readBooks(){
System.out.prinln(“사람이 책을 읽습니다")
}
}
class Tiger extends Animal {
public void move(){
System.out.println(“호랑이가 네 발로 뜁니다 ");
}
public void hunting(){
System.out.println(“호랑이가 사냥을 합니다")
}
}
class Eagle extends Animal {
public void move(){
System.out.println(“독수리가 하늘을 날아갑니다");
}
public void wander(){
System.out.println(“독수리가 하늘을 떠돕니다")
}
}
public class AnimalTest {
public static void main(String[] args){
/// Animal이라는 Type 하나로 만들고
Animal hAnimal = new Human();
Animal tAnimal = new Tiger();
Animal eAnimal = new Eagle();
AnimalTest test = new AnimalTest();
/// 다형성 방법 1 : ArrayList에 추가 후 for문 하나로 다양한 구현(다양한 타입)
/// 코드 재사용의 한 방법
ArrayList<Animal> animalList = new ArrayList<Animal>();
animalList.add(hAnimal);
animalList.add(tAnimal);
animalList.add(eAnimal);
for( Animal animal : animalList ){
animal.move();
}
}
/// 다형성 방법2 : 상속 > 형변환 > 오버라이딩 > 가상함수 > 다형성
public void moveAnimal(Animal animal){
animal.move();
}
}
| 다형성의 사용으로써 갖는 장점
다양한 여러 클래스를 하나의 자료형(상위 클래스)로 선언하거나
형변환 하여 각 클래스가 동일한 메서드를 오버라이딩 한 경우,
하나의 코드가 다양한 구현을 실행할 수 있음
유사한 클래스가 추가되는 경우 유지보수에 용이하고
각 자료형 마다 다른 메서드를 호출하지 않으므로 코드에서 많은 if 문이 사라짐
Ex) 위 CustomerTest를 통한 이해
기존 Customer, VIPCustomer만이 아닌 GoldCustomer 클래스를 추가 시,
grade에 대한 기준을 위해서 수 많은 if문이 들어가야하지만
Customer 클래스에서 상속을 받아서 오버라이딩 해야하는 부분만 해준다면
많은 메서드에 대한 if문에 추가가 필요가 없고,
이후에 다른 grade ( PlatinumCustomer, RubyCustomer )에 따른 클래스 추가에도
훨씬 적은 코드로 추가하기 편함
| 상속은 언제 사용할까?
IS-A 관계 ( is ar relationship : inheritance )
일반적인(General) 개념과 구체적인(Specific) 개념과의 관계
상위 클래스 : 일반적인 개념 클래스 ( 예 : 포유류 )
하위 클래스 : 구체적인 개념 클래스 ( 예 : 사람, 원숭이, 고래, 등 )
단순히 코드를 재사용하는 목적으로 사용하지 않음
HAS-A 관계 ( Composition )
한 클래스가 다른 클래스를 소유한 관계
코드 재사용의 한 방법
Ex) Student가 Subject을 포함한 관계
—-----------—-----------—-----------—-----------—-----------—-----------—-----------—-----------
—-----------—-----------—-----------—-----------—-----------—-----------—-----------—-----------
// 15. 다운 캐스팅과 instanceof
| 하위 클래스로 형 변환, 다운캐스팅
묵시적으로 상위 클래스 형변환된 인스턴스가
원래 자료형(하위클래스)로 변환되어야 할 때 다운캐스팅이라 함
하위 클래스로의 형 변환은 명시적으로 되어야 함
Customer vc = new VIPCustomer(); // 묵시적
VIPCustomer vCustomer = (VIPCustomer)vc; // 명시적
| instanceof
True, False를 반환하는
/// 캐스팅 오류 & instanceof 사용 설명
/// AnimalTest.java /// ( 위 파일에서 일부 첨부 )
public class AnimalTest {
public static void main(String[] args){
Animal hAnimal = new Human();
Animal tAnimal = new Tiger();
Animal eAnimal = new Eagle();
/// human이라는 변수로 다운캐스팅
Human human = (Human)hAnimal;
human.readBook();
/// 잘못된 캐스팅 오류의 예
// Eagle human = (Eagle)hAnimal;
/// 잘못된 캐스팅을 하는 것을 방지하는 구문 instanceof
if (hAnimal instanceof Human){
Human human = (Human)hAnimal;
human.readbooks();
}
AnimalTest test = new AnimalTest();
ArrayList<Animal> animalList = new ArrayList<Animal>();
animalList.add(hAnimal);
animalList.add(tAnimal);
animalList.add(eAnimal);
for( Animal animal : animalList ){
animal.move();
}
}
/// 다운 캐스팅
public class AnimalTest
{
public static void main(String[] args)
{
Animal hAnimal = new Human();
Animal tAnimal = new Tiger();
Animal eAnimal = new Eagle();
ArrayList<Animal> animalList = new ArrayList<Animal>();
animalList.add(hAnimal);
animalList.add(tAnimal);
animalList.add(eAnimal);
AnimalTest test = new AnimalTest();
test.testDownCasting(animalList);
}
public void testDownCasting(ArrayList<Animal> list)
{
for(int i = 0; i<list.size(); i++)
{
Animal animal = list.get(i);
if( animal instanceof Human)
{
Human human = (Human)animal;
human.readBooks();
}
else if( animal instanceof Tiger)
{
Tiger tiger = (Tiger)tiger;
tiger.hunting();
}
else if( animal instanceof Eagle)
{
Eagel eagler = (Eagle)animal;
eagler.wander();
}
else
{
System.out.println(“Not Defined”);
}
}
}
}
—-----------—-----------—-----------—-----------—-----------—-----------—-----------—-----------
—-----------—-----------—-----------—-----------—-----------—-----------—-----------—-----------
/// 16. 코딩해보세요
| 일반 고객와 VIP 고객의 중간 등급 만들기
고객이 늘어 VIP 고객만큼 물건을 많이 구입하지 않지만,
단골인 분들을 GOLD 등급으로 관리하고 싶습니다.
혜택은 다음과 같습니다.
- 제품을 살 때는 항상 10%를 할일해 줍니다.
- 보너스 포인트는 2%를 적립해 줍니다.
- 담당 전문 상담원은 없습니다.
Customer 클래스에서 상속을 받아 GoldCustomer를 구현해 보세요
| 배열을 활용하여 구현하기
고객은 현재 5명입니다.
VIP 1명, GOLD 2명, SILVER 2명 일때, 각 고객이 10,000원짜리 제품을 구매한 경우
지불한 금액과 적립된 보너스 포인트를 출력해보세요
ArrayList를 활용하여 구현해봅니다.
—-----------—-----------—-----------—-----------—-----------—-----------—-----------—-----------
—-----------—-----------—-----------—-----------—-----------—-----------—-----------—-----------
이 정리 내용은 패스트 캠퍼스, K-Digital Credit Java 강의를 참고했습니다