220607_인터넷 강의_상속, 형 변환, 오버라이딩

창고·2022년 10월 20일
0

티스토리에 저장했던 글을 옮겼습니다.
https://mrcocoball.tistory.com/79

1. 클래스 상속

  • 이미 구현된 클래스보다 더 구체적인 기능을 가진 클래스를 구현해야 할 때 기존 클래스를 상속
  • 상위 클래스는 하위 클래스보다 더 일반적인 개념과 기능을 가짐
  • 하위 클래스는 상위 클래스보다 더 구체적인 개념과 기능을 가짐
  • Customer (상위 클래스) - VIPCustomer (하위 클래스) 예시
public class Customer {
	
	// private int customerID;
	// private String customerName;
	// private String customerGrade;
	protected int customerID; // 상속된 자식 클래스에서도 접근할 수 있게 protected
	protected String customerName; // 상속된 자식 클래스에서도 접근할 수 있게 protected
	protected String customerGrade; // 상속된 자식 클래스에서도 접근할 수 있게 protected
	int bonusPoint;
	double bonusRatio;
	
	
	// constructor
	
	public Customer() {
		customerGrade = "SILVER";
		bonusRatio = 0.01;
	}
	
	// VIPCustomer에 대한 필드와 메소드를 넣기에는 부적절 (if, else문도 계속 많아질 것) -> 상속!!
	
	// getter and 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;
	}
	
	
	// method
	
	public int calcPrice(int price) {
		bonusPoint += price * bonusRatio;
		return price;
	}
	

	public String showCustomerInfo() {
		return customerName + "님의 등급은 " + customerGrade + "이며, 보너스 포인트는 " + bonusPoint + "입니다.";
	}

}
public class VIPCustomer extends Customer { // Customer 상속 받음
	
	private int agentID;
	double salesRatio;
	
	// constructor
	
	public VIPCustomer() {
		
		bonusRatio = 0.05;
		salesRatio = 0.1;
		// customerGrade = "VIP"; Customer의 customerGrade가 private이기 때문에 접근 불가
		customerGrade = "VIP";
	}
	
	
	// getter and setter

	public int getAgentID() {
		return agentID;
	}

	public void setAgentID(int agentID) {
		this.agentID = agentID;
	}
}
public class CustomerTest {

	public static void main(String[] args) {
		
		Customer customerRico = new Customer();
		customerRico.setCustomerName("리코");
		customerRico.setCustomerID(10010);
		customerRico.bonusPoint = 1000;
		System.out.println(customerRico.showCustomerInfo());
		
		VIPCustomer customerMari = new VIPCustomer();
		customerMari.setCustomerName("마리");
		customerMari.setCustomerID(10011);
		customerMari.bonusPoint = 10000;
		System.out.println(customerMari.showCustomerInfo());
	}

}

2. 상속에서 클래스 생성 과정과 형 변환

(1) 하위 클래스가 생성되는 과정

  • 하위 클래스 생성 시 상위 클래스가 먼저 생성딤
  • 클래스가 상속 받은 경우 하위 클래스의 생성자에서는 반드시 상위 클래스의 생성자를 호출

(2) super 키워드

  • 하위 클래스에서 가지는 상위 클래스에 대한 참조 값
  • super() 는 상위 클래스의 기본 생성자를 호출
  • 하위 클래스에서 명시적으로 상위 클래스의 생성자를 호출하지 않으면 super() 호출
    (이 때 반드시 상위 클래스의 기본 생성자가 존재해야 함)
  • 상위 클래스의 기본 생성자가 없는 경우 (다른 생성자가 있는 경우)
    하위 클래스에서는 생성자에서는 super를 이용하여 명시적으로 상위 클래스의 생성자를 호출
  • super는 생성된 상위 클래스 인스턴스의 참조 값을 가지므로 super를 이용하여 상위 클래스의 메서드나 멤버 변수에 접근할 수 있음
	// constructor
	
	/* public VIPCustomer() { //Customer() 기본 생성자가 없으므로 오류 발생
		
		bonusRatio = 0.05;
		salesRatio = 0.1;
		// customerGrade = "VIP"; Customer의 customerGrade가 private이기 때문에 접근 불가
		customerGrade = "VIP";
		
		System.out.println("VIPCustomer() 생성자 호출");
	} */
	
	public VIPCustomer() {		
		
		super(0, "no-name");
		bonusRatio = 0.05;
		salesRatio = 0.1;
		// customerGrade = "VIP"; Customer의 customerGrade가 private이기 때문에 접근 불가
		customerGrade = "VIP";
		
	}
	
	public VIPCustomer(int customerID, String customerName) {
		super(customerID, customerName);
		bonusRatio = 0.05;
		salesRatio = 0.1;
		// customerGrade = "VIP"; Customer의 customerGrade가 private이기 때문에 접근 불가
		customerGrade = "VIP";
		
	}

(3) 상속에서의 인스턴스 메모리의 상태

  • 항상 상위 클래스의 인스턴스가 먼저 생성되고 이후 하위 클래스의 인스턴스가 생성

(4) 형 변환 (업캐스팅)

  • 상위 클래스로 변수를 선언, 하위 클래스의 생성자로 인스턴스를 생성
부모타입 변수 = new 자식타입 생성자
Customer customerMari = new VIPCustomer();
  • 상위 클래스 타입의 변수에 하위 클래스 변수가 대입
VIPCustomer vCustomer = new VIPCustomer();
addCustomer(vCustomer);
int addCustomer(Customer customer){
}
  • 하위 클래스는 상위 클래스의 타입을 내포하고 있어 상위 클래스로의 묵시적 형 변환이 가능
  • 상속 관계에서 모든 하위 클래스는 상위 클래스로 형 변환(업캐스팅)이 되며 그 역은 성립 X
		Customer vc = new VIPCustomer(12345, "noname"); // 형변환 진행 vc의 타입은 Customer이지만 인스턴스는 VIPCustomer()임
		price = vc.calcPrice(1000); // 재정의된 calcPrice 적용 (인스턴스의 메소드)
		System.out.println(price); 

3. 메소드 재정의하기 (오버라이딩)

  • 상위 클래스에 정의된 메서드의 구현 내용이 하위 클래스에서 구현할 내용과 맞지 않는 경우
    하위 클래스에서 동일한 이름의 메서드를 재정의 할 수 있음
	@Override // Customer의 calcPrice 재정의(오버라이딩)
	// source -> override로 활성화 가능
	public int calcPrice(int price) {
		bonusPoint += price * bonusRatio;
		return price - (int)(price * salesRatio); // 해당 부분 재정의
	}
public class CustomerTest {

	public static void main(String[] args) {
		
		Customer customerRico = new Customer(10010, "리코");
		customerRico.bonusPoint = 1000;
		int price = customerRico.calcPrice(1000);
		System.out.println(customerRico.showCustomerInfo() + price);
		
		VIPCustomer customerMari = new VIPCustomer(10011, "마리");
		customerMari.bonusPoint = 10000;
		price = customerMari.calcPrice(1000);
		System.out.println(customerMari.showCustomerInfo() + price);		
	}
}

  • 형 변환과 오버라이딩 메소드 호출 : vc의 타입은 Customer지만 인스턴스의 타입은 VIPCustomer. 자바에서는 항상 인스턴스의 메서드가 호출 되며 자바의 모든 메서드는 가상 메서드(virtual method)
		Customer vc = new VIPCustomer(12345, "noname"); // 형변환 진행 vc 변수의 타입은 Customer이지만 인스턴스는 VIPCustomer()임
		price = vc.calcPrice(1000); // 재정의된 calcPrice 적용 (인스턴스의 메소드)
		System.out.println(price); 

4. 메소드 재정의와 가상 메소드 원리 ***

(1) 메소드의 호출과 실행

  • 메소드(함수)의 이름은 주소값을 나타냄
  • 메소드는 명령어의 set 이고 프로그램이 로드되면 메소드 영역 (코드 영역)에 명령어 set 위치
  • 해당 메소드 호출 시 명령어 set이 있는 주소를 찾아 명령어 실행
  • 메소드에 사용하는 변수들은 스택 메모리에 위치
  • 그러므로 다른 인스턴스라도 같은 메소드의 코드는 같으므로 같은 메소드가 호출됨
  • 인스턴스가 생성되면 변수는 힙 메모리에 따로 생성되나 메소드 명령어 set은 처음 한번만 로드

(2) 가상 메소드의 원리

  • 가상 메소드 테이블에서 해당 메소드에 대한 주소를 가지고 있음
  • 재정의된 경우 재정의 된 메소드의 주소를 가리킴

5. 다형성(polymorphism) 이란?

  • 하나의 코드가 여러 자료형으로 구현되어 실행되는 것
  • 같은 코드에서 여러 다른 실행 결과가 나옴
  • 정보 은닉, 상속과 더불어 객체 지향 프로그래밍의 가장 큰 특징 중 하나
  • 다형성 잘 활용 시 유연하고 확장성 있고 유지보수가 편리한 프로그램 제작 가능
  • 상위 클래스에서 공통적인 부분을 제공, 하위 클래스에서 각 클래스에 맞는 기능 구현
  • 여러 클래스를 하나의 타입(상위 클래스)로 핸들링 가능
class Idol {
	
	public void call() {
		System.out.println("아이돌이 콜 앤드 리스폰스합니다.");
	}
	
	public void dancing() {
		
	}
	
}

class Rico extends Idol {
	
	@Override
	public void call() {  // 재정의
		System.out.println("리코가 콜 앤드 리스폰스합니다.");
	}
	
	public void raser() {
		System.out.println("리코가 레이저 빔을 쏩니다.");
	}
}

class Mari extends Idol {
	
	@Override
	public void call() {  // 재정의
		System.out.println("마리가 콜 앤드 리스폰스합니다.");
	}
	
	public void money() {
		System.out.println("마리가 돈을 씁니다.");
	}
}

class Yoshiko extends Idol {
	
	@Override
	public void call() { // 재정의
		System.out.println("요시코가 콜 앤드 리스폰스합니다.");
	}
	
	public void fallen() {
		System.out.println("요시코가 타천합니다.");
	}
}
public class IdolTest {
	

	public static void main(String[] args) {
		
		Idol rIdol = new Rico(); // 형변환
		Idol mIdol = new Mari(); // 형변환
		Idol yIdol = new Yoshiko(); // 형변환
		
		IdolTest test = new IdolTest();
		test.callIdol(rIdol);
		test.callIdol(mIdol);
		test.callIdol(yIdol);
		
		ArrayList<Idol> idolList = new ArrayList<>();
		idolList.add(rIdol);
		idolList.add(mIdol);
		idolList.add(yIdol);
		
		for (Idol idol : idolList) {
			idol.call();
		}
				
	}
	
	public void callIdol(Idol idol) { // Idol 타입의 하위 클래스 인스턴스에 따라 재정의한 call() 메소드 사용
		idol.call();
		// idol.fallen(); 안됨 (상위 클래스에 없으며 재정의되지 않았음) >> 사용하려면 다운 캐스팅 필요
		// idol.money(); 안됨 (상위 클래스에 없으며 재정의되지 않았음) >> 사용하려면 다운 캐스팅 필요
		// idol.raser(); 안됨 (상위 클래스에 없으며 재정의되지 않았음) >> 사용하려면 다운 캐스팅 필요
	}


}
  • [Customer.java] - 상위(부모 클래스)
public class Customer {
	
	// private int customerID;
	// private String customerName;
	// private String customerGrade;
	protected int customerID; // 상속된 자식 클래스에서도 접근할 수 있게 protected
	protected String customerName; // 상속된 자식 클래스에서도 접근할 수 있게 protected
	protected String customerGrade; // 상속된 자식 클래스에서도 접근할 수 있게 protected
	int bonusPoint;
	double bonusRatio;
	
	
	// constructor
	
	public Customer(int customerID, String customerName) {
		this.customerID = customerID;
		this.customerName = customerName;
		customerGrade = "SILVER";
		bonusRatio = 0.01;	
		
	}
	
	// VIPCustomer에 대한 필드와 메소드를 넣기에는 부적절 (if, else문도 계속 많아질 것) -> 상속!!
	
	// getter and 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;
	}
	
	
	// method
	
	public int calcPrice(int price) {
		bonusPoint += price * bonusRatio;
		return price;
	}
	

	public String showCustomerInfo() {
		return customerName + "님의 등급은 " + customerGrade + "이며, 보너스 포인트는 " + bonusPoint + "입니다.";
	}

}
  • [GoldCustomer.java]
public class GoldCustomer extends Customer {
	
	double salesRatio;
	
	// constructor
	
	public GoldCustomer(int customerID, String customerName) {
		super(customerID, customerName);
		bonusRatio = 0.02;
		salesRatio = 0.1;
		// customerGrade = "VIP"; Customer의 customerGrade가 private이기 때문에 접근 불가
		customerGrade = "GOLD";
		
	}
	
	
	@Override // Customer의 calcPrice 재정의(오버라이딩)
	// source -> override로 활성화 가능
	public int calcPrice(int price) {
		bonusPoint += price * bonusRatio;
		return price - (int)(price * salesRatio); // 해당 부분 재정의
	}
}
  • [VIPCustomer.java]
public class VIPCustomer extends Customer { // Customer 상속 받음
	
	private int agentID;
	double salesRatio;
	
	// constructor
	
	public VIPCustomer(int customerID, String customerName, int agentID) {
		super(customerID, customerName);
		bonusRatio = 0.05;
		salesRatio = 0.1;
		this.agentID = agentID;
		// customerGrade = "VIP"; Customer의 customerGrade가 private이기 때문에 접근 불가
		customerGrade = "VIP";
		
	}
	
	// getter and setter

	public int getAgentID() {
		return agentID;
	}

	public void setAgentID(int agentID) {
		this.agentID = agentID;
	}
	
	@Override // Customer의 calcPrice 재정의(오버라이딩)
	// source -> override로 활성화 가능
	public int calcPrice(int price) {
		bonusPoint += price * bonusRatio;
		return price - (int)(price * salesRatio); // 해당 부분 재정의
	}
	
	@Override
	public String showCustomerInfo() {
		return super.showCustomerInfo() + " 담당 상담원 번호는 " + agentID + " 입니다."; // super로 상위 클래스 메소드에 접근
	}

}
  • [CustomerTest.java]
public class CustomerTest {

	public static void main(String[] args) {
		
		ArrayList<Customer> customerList = new ArrayList<>();
		
		Customer customerH = new Customer(10010, "하나마루");
		Customer customerY = new Customer(10011, "요시코");
		Customer customerR = new GoldCustomer(10012, "리코");
		Customer customerD = new GoldCustomer(10013, "다이아");		
		Customer customerM = new VIPCustomer(10014, "마리", 12345);
		
		customerList.add(customerH);
		customerList.add(customerY);
		customerList.add(customerR);
		customerList.add(customerD);
		customerList.add(customerM);
		
		System.out.println("------------ 고객 정보 ------------");
		
		for (Customer customer : customerList) {
			System.out.println(customer.showCustomerInfo());
		}
		
		int price = 10000;
		
		System.out.println("------------ 구매 정보 ------------");
		
		for (Customer customer : customerList) {
			int cost = customer.calcPrice(price);
			System.out.println(customer.getCustomerName() + "님이 " + cost + "원 지불하셨습니다.");
			System.out.println(customer.getCustomerName() + "님의 현재 보너스 포인트는 " + customer.bonusPoint + "입니다.");
		}
	
	}
}

6. 상속의 사용 시기

(1) IS-A 관계 (is a relationship : inheritance)

  • 일반적(general) 개념과 구체적(specific) 개념과의 관계
  • 상위 클래스 (Employee) 와 하위 클래스 (Engineer, Manager)
  • 클래스 간의 결합도가 높은 설계
  • 상위 클래스의 수정이 많은 하위 클래스에 영향을 미칠 수 있음
  • 계층 구조가 복잡하거나 hierachy가 높으면 좋지 않음

(2) HAS-A 관계 (composition)

  • 클래스가 다른 클래스를 포함하는 관계 (변수로 선언)
  • 코드 재사용의 가장 일반적인 방법
  • Student가 Subject를 포함하는, Library 구현 시 ArrayList 생성 활용
  • 상속하지 않음
profile
공부했던 내용들을 모아둔 창고입니다.

0개의 댓글