상속과 다형성
- 상속이란?
- 클래스를 정의 할 때 이미 구현된 클래스를 상속(inheritance)받아서 속성이나 기능이 확장되는 클래스를 구현
- 상속하는 클래스: 상위 클래스,parent class,base class, super class
- 상속받는 클래스: 하위 클래스, childe class, derived class, subclass
- 상위 클래스는 하위 클래스보다 일반적인 의미를 가짐
- 하위 클래스는 상위 클래스 보다 구체적인 의미를 가짐
- 클래스를 정의 할 때 이미 구현된 클래스를 상속(inheritance)받아서 속성이나 기능이 확장되는 클래스를 구현
- 클래스 상속 문법
//클래스 B는 클래스 A의 상속을 받는다.
class B extends A{
}
- 상속의 예시
Class Mammal{
}
class Human extends Mammal{
}
- 포유류 클래스는 인간 클래스에게 상속한다// 인간 클래스가 포유류 클래스를 상속 받는다.
- extends 뒤에는 단 하나의 class만 사용할 수 있음
- 자바는 single inheritance만을 지원 함.
- 코드의 재사용 방법이 무조건 상속은 아님
- 상속은 general 과 specific 의 관계
- 상속 이미 있는 클래스를 가져다 쓰는 것이 아니라 일반적인 보편적인 클래스가 정의되어있고, 그 기능을 가져다 쓰면서 좀 더 구체적인 클래스를 만들 때 상속을 사용
- 상속은 IS-A(이즈어) 관계
- 코드의 재사용 방법에는 합성도 있음
- 합성도 있음, 합성은 Has-A(해즈어) 관계
- 상속은 general 과 specific 의 관계
- Has-A 관계, 코드 재사용 합성의 예시
//Point 클래스
package inheritance;
public class Point {
private int x;
private int 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;
}
}
// Circle 클래스
package inheritance;
public class Circle {
Point point; //has - a 관계
private int radius;
public Circle() {
point = new Point();
}
}
- 상속을 활용한 고객관리 프로그램 만들기
- 고객의 정보를 활용하여 고객 맞춤 서비스를 구현
- 고객의 등급에 따라 차별화 된 할인율과 포인트를 지금
- 등급에 따른 클래스를 따로 구현하는 것이 아닌 일반적인 클래스를 먼저 구현하고 그 보다 기능이 많은 클래스는 상속을 활용하여 구현함
- Customer 클래스
package inheritance;
public class Customer {
// 멤버 변수
private int customerID;// 고객 아이디
private String customerName;// 고객 이름
private String customerGrade;// 고객 등금
int bonusPoint;// 보너스 포인트
double bonusRatio;// 적립 비율
// 디폴트 생성자
public Customer() {
customerGrade = "Silver";// 기본 등급
bonusRatio = 0.01;// 보너스 포인트 기본 적립 비율
}
/*
* //매개변수 생성자 public Customer(int customerID,String customerName) {
* this.customerID=customerID; this.customerName=customerName;
* customerGrade="Silver"; bonusRatio=0.01; }
*/
// 보너스 포인트 적립,지불 가격 계산 메서드
public int calcPrice(int price) {
bonusPoint += price * bonusRatio;// 보너스 포인트 계산
return price;
}
// 고객 정보를 반환하는 메서드
public String showCustomerInfo() {
return customerName + "님의 등급은" + customerGrade + "이며, 보너스포인트는" + bonusPoint + "입니다.";
}
- 상속받을 VipCustomer 구현
package inheritance;
public class VIPCustomer {
//Customer 클래스와 겹치는 멤버 변수
private int customerID;
private String customerName;
private String customerGrade;
int bonusPoint;
double bonusRatio;
//Vip 고객 관련 기능을 구현할 때만 필요한 멤버 변수
private int agentID;
double saleRatio;
//디폴트 생성자
public VIPCustomer() {
customerGrade="VIp";
bonusRatio=0.05;
saleRatio=0.1;
}
public int calcPrice(int price) {
bonusPoint+=price*bonusRatio;
return price-(int)(price*saleRatio); // 할인율 적용
}
//VIP 고객에게만 필요한 메서드
public int getAgentID() {
return agentID;
}
//
public String showCustomerInfo() {
return customerName+"님의 등급은"+customerGrade+"이며, 보너스 포인트는"+bonusPoint+"입니다.";
}
}
- 앞에서 만든 Customer 클래스와 겹치는 멤버 변수와 메서드 존재,이름은 같은데 구현 내용이 다른 메서드 존재
- 상속 사용
package inheritance;
//VIPCustomer클래스는 Customer클래스를 상속받음
public class VIPCustomer extends Customer {
// Vip 고객 관련 기능을 구현할 때만 필요한 멤버 변수
private int agentID;
double saleRatio;
// 디폴트 생성자
public VIPCustomer() {
customerGrade = "VIp";
bonusRatio = 0.05;
saleRatio = 0.1;
}
// VIP 고객에게만 필요한 메서드
public int getAgentID() {
return agentID;
}
}
++ 오류 2가지 발생
1) customerGrade 변수에서 private 변수로 인한 문제 발생
2) VIP 고객에게 제공하는 혜택인 할인율과 세일 가격을 어떻게 적용할지 구현 X
1을 해결하기 위해 변수들 protected 로 선언 변경 후 protected를 선언한 변수들을 사용하기위해 get(),set() 메서드 추가
package inheritance;
public class Customer {
// 멤버 변수
protected int customerID;// 고객 아이디
protected String customerName;// 고객 이름
protected String customerGrade;// 고객 등금
int bonusPoint;// 보너스 포인트
double bonusRatio;// 적립 비율
// 디폴트 생성자
public Customer() {
customerGrade = "Silver";// 기본 등급
bonusRatio = 0.01;// 보너스 포인트 기본 적립 비율
}
/*
* //매개변수 생성자 public Customer(int customerID,String customerName) {
* this.customerID=customerID; this.customerName=customerName;
* customerGrade="Silver"; bonusRatio=0.01; }
*/
// 보너스 포인트 적립,지불 가격 계산 메서드
public int calcPrice(int price) {
bonusPoint += price * bonusRatio;// 보너스 포인트 계산
return price;
}
// 고객 정보를 반환하는 메서드
public String showCustomerInfo() {
return customerName + "님의 등급은" + customerGrade + "이며, 보너스포인트는" + bonusPoint + "입니다.";
}
//getter setter 메서드
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;
}
}
--실행해보기
package inheritance;
public class CustomerTest1 {
public static void main(String[] args) {
Customer customerLee=new Customer();
customerLee.setCustomerID(10010);
customerLee.setCustomerName("이순신");
customerLee.bonusPoint=1000;
System.out.println(customerLee.showCustomerInfo());
VIPCustomer customerKim = new VIPCustomer();
customerKim.setCustomerID(10020);
customerKim.setCustomerName("김유신");
customerKim.bonusPoint=10000;
System.out.println(customerKim.showCustomerInfo());
}
}
- 접근 제한자 가시성
외부클래스 | 하위 클래스 | 동일 패키지 | 내부 클래스 | |
public | O | O | O | O |
protected | X | O | O | O |
선언되지 않음 (default) |
X | X | O | O |
private | X | X | X | O |
- 상속에서 클래스 생성 과정
- 하위 클래스가 생성될 때 상위 클래스가 먼저 생성 됨
- 상위 클래스의 생성자가 호출되고 하위 클래스의 생성자가 호출 됨
- 하위 클래스의 생성자에서는 무조건 상위 클래스의 생성자가 호출되어야 함.
- 아무것도 없는 경우 컴파일러는 상위 클래스 기본 생성자를 호출하기 위한 super()를 코드에 넣어 줌
- super()호출되는 생성자는 상위 클래스의 기본 생성자임
- 만약 상위클래스의 기본생성자가 없는 경우(매개변수가 있는 생성자만 존재하는 경우) 하위 클래스는 명시적으로 상위클래스를 호출해야함.
- customer 클래스 생성자 안에 System.out.println("Customer()생성자 호출");을 추가하고
- VIPcustomer 클래스 생성자 안에 System.out.println("VIPCustomer()생성자 호출"); 를 추가하고 Customer Test1을 실행시켜보면
package inheritance;
public class CustomerTest1 {
public static void main(String[] args) {
Customer customerLee=new Customer();
customerLee.setCustomerID(10010);
customerLee.setCustomerName("이순신");
customerLee.bonusPoint=1000;
System.out.println(customerLee.showCustomerInfo());
System.out.println("=====================");
VIPCustomer customerKim = new VIPCustomer();
customerKim.setCustomerID(10020);
customerKim.setCustomerName("김유신");
customerKim.bonusPoint=10000;
System.out.println(customerKim.showCustomerInfo());
}
/*결과:
Customer()생성자 호출
이순신님의 등급은Silver이며, 보너스포인트는1000입니다.
=====================
Customer()생성자 호출
VIPCustomer()생성자 호출
김유신님의 등급은VIP이며, 보너스포인트는10000입니다.
*/
}
- 결과에서 하위클래스가 생성될떄 상위클래스가 먼저 생성되는 것을 확인할 수 있다.
- 컴파일러가 super(); 키워드를 VIPCusomter()안에 최상위에 넣어놓기 때문.
- super 예약어는 하위클래스에서 상위클래스로 접근할 때 사용 .
- 하위클래스는 상위클래스의 주소 참조값을 알고있고 이 참조 값을 가지고 있는 예약어가 바로 super.
- 사실 Customer() 생성자도 super(); 가 들어있음 최상위 클래스 Object 를 우리가 알게모르게 상속받고 있기떄문에;
- 그렇기 떄문에 상위 클래스에 멤버변수들이 메모리에 생성되고, 하위 클래스가 사용할수 있다.
- 상속에서의 메모리 상태
- 상위 클래스의 인스턴스가 먼저 생성이 되고, 하위클래스의 인스턴스가 생성됨.
- 상위 클래스의 인스턴스가 먼저 생성이 되고, 하위 클래스의 인스턴스가 생성
- 만일 상위 클래스가 private 이면 생성은 되지만 쓸 수가 없을 뿐임.
- 상위클래스에서 매개변수 생성자를 만들고, 기본생성자가 없을 경우 하위 클래스에서 명시적으로 super(상위클래스에 매개변수를 초기화 하거나 적어줘야함
예시
package inheritance;
//VIPCustomer클래스는 Customer클래스를 상속받음
public class VIPCustomer extends Customer {
// Vip 고객 관련 기능을 구현할 때만 필요한 멤버 변수
private int agentID;
double saleRatio;
// 디폴트 생성자
public VIPCustomer(int customerID, String customerName) {
super(customerID,customerName);
customerGrade = "VIP";
bonusRatio = 0.05;
saleRatio = 0.1;
System.out.println("VIPCustomer()생성자 호출");
}
// VIP 고객에게만 필요한 메서드
public int getAgentID() {
return agentID;
}
}
- super 예약어
- this가 자기 자신의 인스턴스의 주소를 가지는 것처럼 super는 하위클래스가 상위클랫에 대한 주소를 가지게 됨
- 하위 클래스가 상위 클래스에 접근할때 사용할 수 있음.
- 상위 클래스로의 묵시적 형 변환(업캐스팅)
- 상위 클래스 형으로 변수를 선언하고 하위 클래스 인스턴스를 생성할 수 있음
- 하위 클래스는 상위 클래스의 타입을 내포하고 있으므로 상위 클래스로 묵시적 형 변환이 가능함.
- 모든 하위 클래스는 상위 클래스 자료형으로 변환 된 수 있지만 그 역은 성립하지 않음
Customer vc = new VIPCustomer();
//선언된 클래스형(상위 클래스형) = 생성된 인스턴스의 클래스형(하위 클래스형)
- 형 변환에서의 메모리
- Customer vc = new VIPCustomer();에서 vc 가 가리키는 것
- - VIPCustomer()생성자의 호출로 인스턴스는 모두 생성 되 었지만
- 타입이 Customer 이므로 접근할 수 있는 변수나 메서드는 Customer의 변수와 메서드임.
- 왜 이렇게 쓰는가?
- 메서드 오버라이딩(OverRiding)
- 상위 클래스에서 정의 된 메서드 중 하위 클래스와 기능이 맞지 않거나 추가 기능이 필요한 경우 같은 이름과 매개변수로 하위 클래스에서 재정의함.
- 오버라이딩을 하려면 반환형, 메서드 이름, 매개변수 개수, 매개변수 자료형이 반드시 같아야 함. 그렇지 않으면 자바 컴파일러는 재정의 한 메서드를 기존 메서드와 다른 메서드로 인식함.
- 오버로딩 vs 오버라이딩
- 오버로딩은 이름이 똑같은 method가 여러개 있는 것
- 오버라이딩은 덮어썻다. 로 생각하면 좀 외우기 쉬울것
package inheritance;
public class OverridingTest {
public static void main(String[] args) {
Customer customerLee = new Customer(100010,"LEE");
int price = customerLee.calcPrice(10000);
System.out.println("지불 금액은"+ price+"이고"+customerLee.showCustomerInfo());
VIPCustomer customerKim = new VIPCustomer(100010, "Kim",100);
price = customerKim.calcPrice(10000);
System.out.println("지불 금액은"+ price+"이고"+ customerKim.showCustomerInfo());
}
}
/*결과:
지불 금액은10000이고LEE님의 등급은Silver이며, 보너스포인트는100입니다.
지불 금액은9000이고Kim님의 등급은VIP이며, 보너스포인트는500입니다.
*/
__
++ 가상메서드(virtual method)
프로그램에서 어떤 객체의 변수나 메서드의 참조는 그 타입에 따라 이루어 짐.
가상 메서드의 경우는 타입과 상관없이 실제 생성된 인스턴스의 메서드가 호출 되는 원리
package inheritance;
public class OverridingTest {
public static void main(String[] args) {
Customer customerWho = new VIPCustomer(10010,"Who",100);
int price = customerWho.calcPrice(10000);
System.out.println("지불 금액은"+ price+"이고"+ customerWho.showCustomerInfo());
}
}
/*결과
지불 금액은9000이고Who님의 등급은VIP이며, 보너스포인트는500입니다.
*/
- 상위 클래스로 묵시적형변환을 하게 되었음.
-상위클래스에 멤버에만 접근 할 수 있는데 계산은 하위클래스에 calcPrice가 계산됨.
why? 가상함수의 원리: 재정의 되면 재정의 되어있는 메서드가 불림
- 타입이 아니라 인스턴스에 해당하는 메서드가 호출됨.
- 다형성 구현
---------------
다형성(polymorphism)
다형성: 하나의 코드가 여러가지 자료형으로 구현되어 실행되는 것, 정보 은닉, 상속과 더불어 객체지향 프로그래밍의 가장 큰 특징 중하나, 객체 지향 프로그래밍의 유연성,재활용성,유지 보수성에 기본이 되는 특징
예시) 동물 클래스/ 동물을 상속받은 사람 클래스/ 동물을 상속받은 호랑이 클래스/ 동물을 상속받은 독수리 클래스를 만듦
package polymorphism;
public class Animal {
public void move() {
System.out.println("동물이 움직입니다.");
}
}
package polymorphism;
public class Human extends Animal{
public void move() {
System.out.println("사람이 두발로 걷습니다.");
}
}
package polymorphism;
public class Tiger extends Animal {
public void move() {
System.out.println("호랑이가 네발로 뜁니다.");
}
}
package polymorphism;
public class Eagle extends Animal {
public void move() {
System.out.println("독수리가 하늘을 날읍니다.");
}
}
2) 동물이 움직이는 것을 테스트하는 실행 클래스를 만듦
package polymorphism;
public class AnimalTest {
public static void main(String[] args) {
AnimalTest test = new AnimalTest();
test.moveAnimal(new Human());
test.moveAnimal(new Tiger());
test.moveAnimal(new Eagle());
//Animal animal1= new Human();
//animal.move();
//Animal animal2= new Tiger();
//animal2.move();
//Animal animal3= new Eagle();
//animal3.move();
// 같은 거지만 같은 모양을 보여주며 다형성을 확인 시켜주기 위해 위와 아래 코드 사용
}
public void moveAnimal(Animal animal) {
animal.move(); // 코드는 하나 결과는 다양함.
}
}
다형성 구현하기
하나의 클래스를 상속 받은 여러 클래스가 있는 경우
각 클래스마다 같은 이름의 서로 다른 메서드를 재정의함
상위 클래스 타입으로 선언된 하나의 변수가 여러 인스턴스에 대입되어 다양한 구현이 실행 될 수 있음.
---------------------------------------------------------------------------------
다형성 활용하기
일반 고객과 VIP 고객의 중간 등급의 고객을 생성
5명의 고객을 ArrayList에 생성하여 저장한 다음 각 고객이 물건을 샀을 때의 가격과 보너스 포인트를 계산함.
++ 상속을 언제 사용할까?
여러 클래스를 생성하지 않고 하나의 클래스에 공통적인 요소를 모으고 나머지 클래스는 이를 상속받은 다음 각각 필요한 특성과 메서드를 구현하는 방법.
하나의 클래스에 여러 특성을 한꺼번에 구현하는 경우 많은 코드 내 많은 IF 문이 생길 수 있음.
++@어노테이션(Annotation) : 영어로 주석이라는 의
: 컴파일러에게 특정한 정보를 제공해주는 역할
@기호와 함께 사용
++배열에도 araayList에서도 쓸 수 있음.
-----------------------------------------------------------------------------------
다운 캐스팅// instanceof
계층구조 깊게 가는 거 안좋음
package witharraylist;
public class GoldCustomer extends Customer{
double saleRatio;
public GoldCustomer() {
customerGrade="GOLD";
bonusRatio=0.02;
saleRatio=0.1;
}
public GoldCustomer(int customerID, String customerName) {
super(customerID,customerName);
customerGrade="GOLD";
bonusRatio=0.02;
saleRatio=0.1;
}
@Override
public int calcPrice(int price) {
bonusPoint += price*bonusRatio;
return price - (int)(price*saleRatio);
}
}
package witharraylist;
import java.util.ArrayList;
public class CustomerTest {
public static void main(String[] args) {
// 이 ArrayList 안에 Customer 를 상속 받은 모든 종류의객체들이 들어갈수 있음.
ArrayList<Customer> customerList = new ArrayList<Customer>();
Customer customerLee = new Customer(10010, "이순신");
Customer customerShin = new Customer(10011, "신사임당");
GoldCustomer customerHong = new GoldCustomer(10012, "홍길동");
GoldCustomer customerYul = new GoldCustomer(10013, "이율곡");
VIPCustomer customerKim = new VIPCustomer(10014, "김유신", 12345);
customerList.add(customerLee);
customerList.add(customerShin);
customerList.add(customerHong);
customerList.add(customerYul);
customerList.add(customerKim);
System.out.println("=======고객정보 출력=============");
for (Customer customer : customerList) {
System.out.println(customer.showCustomerInfo());
}
/*
* =======고객정보 출력=============
* 이순신님의 등급은Silver이며, 보너스포인트는0입니다.
* 신사임당님의 등급은Silver이며, 보너스포인트는0입니다.
* 홍길동님의 등급은GOLD이며, 보너스포인트는0입니다.
* 이율곡님의 등급은GOLD이며, 보너스포인트는0입니다.
* 김유신님의 등급은VIP이며, 보너스포인트는0입니다.
*/
}
}
package witharraylist;
import java.util.ArrayList;
public class CustomerTest {
public static void main(String[] args) {
// 이 ArrayList 안에 Customer 를 상속 받은 모든 종류의객체들이 들어갈수 있음.
ArrayList<Customer> customerList = new ArrayList<Customer>();
Customer customerLee = new Customer(10010, "이순신");
Customer customerShin = new Customer(10011, "신사임당");
GoldCustomer customerHong = new GoldCustomer(10012, "홍길동");
GoldCustomer customerYul = new GoldCustomer(10013, "이율곡");
VIPCustomer customerKim = new VIPCustomer(10014, "김유신", 12345);
customerList.add(customerLee);
customerList.add(customerShin);
customerList.add(customerHong);
customerList.add(customerYul);
customerList.add(customerKim);
System.out.println("=======고객정보 출력=============");
for (Customer customer : customerList) {
System.out.println(customer.showCustomerInfo());
}
/*
* =======고객정보 출력=============
* 이순신님의 등급은Silver이며, 보너스포인트는0입니다.
* 신사임당님의 등급은Silver이며, 보너스포인트는0입니다.
* 홍길동님의 등급은GOLD이며, 보너스포인트는0입니다.
* 이율곡님의 등급은GOLD이며, 보너스포인트는0입니다.
* 김유신님의 등급은VIP이며, 보너스포인트는0입니다.
*/
System.out.println("=======할인율과 보너스 포인트 결과 =====");
int price=10000;
for(Customer customer: customerList) {
int cost = customer.calcPrice(price);
System.out.println(customer.getCustomerName()+"님이"+cost+"지불하셨습니다.");
System.out.println(customer.showCustomerInfo());
/*
* =======고객정보 출력=============
이순신님의 등급은Silver이며, 보너스포인트는0입니다.
신사임당님의 등급은Silver이며, 보너스포인트는0입니다.
홍길동님의 등급은GOLD이며, 보너스포인트는0입니다.
이율곡님의 등급은GOLD이며, 보너스포인트는0입니다.
김유신님의 등급은VIP이며, 보너스포인트는0입니다.담당상담원 아이디는12345입니다
=======할인율과 보너스 포인트 결과 =====
이순신님이10000지불하셨습니다.
이순신님의 등급은Silver이며, 보너스포인트는100입니다.
신사임당님이10000지불하셨습니다.
신사임당님의 등급은Silver이며, 보너스포인트는100입니다.
홍길동님이9000지불하셨습니다.
홍길동님의 등급은GOLD이며, 보너스포인트는200입니다.
이율곡님이9000지불하셨습니다.
이율곡님의 등급은GOLD이며, 보너스포인트는200입니다.
김유신님이9000지불하셨습니다.
김유신님의 등급은VIP이며, 보너스포인트는500입니다.담당상담원 아이디는12345입니다
*/
}
}
}
-IS-A 관계(is a relationship:inheritance0
일반적인(general)개념과 구체적인(specifi) 개념 과의 관계
상위 클래스: 일반적인 개념클래스(예: 포유류)
하위 클래스: 구체적인 개념클래스(예: 사람,원숭이,고래...)
단순히 코드를 재사용하는 목적으로 사용하지 않음
-HAS-A 관계(composition) 한 클래스가 다른 클래스를 소유한 관계
코드 재사용의 한 방법
Student가 Subject를 포함한 관계
Class Student{
Subject majorSubject;
}
--------------------다운 캐스팅(instanceof)
하위 클래스가 상위 클래스로 변환되는 것은 묵시적으로 이루어짐
다시 원래 자료형의 하위 클래스로 형 변환하려면 명시적으로 다운 캐스팅을 해야함
이때 원래 인스턴스의 타입을 체크하는 예약어가 instaceof임
'Java > Doit!자바프로그래밍입문_개념정리' 카테고리의 다른 글
Java 입문 기초 정리_인터페이스 (0) | 2024.05.09 |
---|---|
Java 입문 기초 정리_ 추상 클래스 (0) | 2024.05.07 |
Java 입문 기초 정리_ 배열과 ArrayList (0) | 2024.05.02 |
Java 입문 기초 정리_ 클래스와 객체 1 (0) | 2024.04.29 |
Java 입문 기초 정리용_제어흐름 이해하기 (0) | 2024.04.26 |