본문 바로가기
Java

Java 객체 지향 핵심 / 상속, 형 변환, 재정의(override), 다형성

by 리잼 2022. 11. 17.
반응형

1. 객체 간의 상속의 의미

클래스 상속

  • 새로운 클래스를 정의 할 때 이미 구현된 클래스를 상속(inheritance)을 받아서 속성이나 기능을 확장하여 클래스를 구현
  • 이미 구현된 클래스보다 더 구체적인 기능을 가진 클래스를 구현해야 할 때 기존 클래스를 상속함

상속 하는 클래스 : 상위클래스, parent class, base class, super class

상속받는 클래스 : 하위 클래스, child class, derived class, subclass

  • 상속의 문법
class B extends A {

}

extends 키워드 뒤에는 단 하나의 클래스만 올 수 있다.

자바는 단일 상속만을 지원함

상속을 구현하는 경우

  • 상위 클래스는 하위 클래스보다 더 일반적인 개념과 기능을 가짐
  • 하위 클래스는 상위 클래스 보다 더 구체적인 개념과 기능을 가짐
  • 하위 클래스가 상위 클래스의 속성과 기능을 확장(extends)한다는 의미

상속 구현의 예


2. 상속을 활용한 멤버십 클래스 구현

멤버십 시나리오

회사에서 고객 정보를 활용한 맞춤 서비스를 하기 위해 일반고객(Customer)과 
이보다 충성도가 높은 우수고객(VIPCustomer)에 따른 서비스를 제공하고자 함

물품을 구매 할때 적용되는 할인율과 적립되는 보너스 포인트의 비율이 다름 
여러 멤버십에 대한 각각 다양한 서비스를 제공할 수 있음
멤버십에 대한 구현을 클래스 상속을 활용하여 구현해보기

일반 고객(Customer)클래스 구현

  • 고객의 속성 : 고객 아이디, 고객 이름, 고객 등급, 보너스 포인트, 보너스 포인트 적립 비율
  • 일반 고객의 경우 물품 구매시 1%의 보너스 포인트 적립

Customer.java

public class Customer {
	
	private int customerId;
	private String customerName;
	private String customerGrade;
	int bonusPoint;
	double bonusRatio;
	
	public Customer() {
		customerGrade = "Silver";
		bonusRatio = 0.01;
	}
	
	public int calcPrice(int price) {
		bonusPoint += price * bonusRatio;
		return price;
	}
	
	public String showCustomerInfo() {
		return customerName + "님의 등급은 " + customerGrade +"이며, 보너스 포인트는 " + bonusPoint +"입니다";
	}
	
}

우수 고객 구현

매출에 더 많은 기여를 하는 단골 고객

제품을 살때 10%를 할인해 줌

보너스 포인트는 제품 가격의 5%를 적립해 줌

담당 전문 상담원이 배정됨
  • Customer 클래스에 추가해서 구현하는것은 좋지 않음
  • VIPCustomer 클래스를 따로 구현
  • 이미 Customer에 구현된 내용이 중복되므로 Customer를 확장하여 구현함 (상속)

VIPCustomer.java

public class VIPCustomer extends Customer {
	
	private int agentID;
	double salesRatio;
	
	public VIPCustomer() {
		customerGrade = "VIP";
		bonusRatio = 0.05;
		salesRatio = 0.1;
	}
	
	public int agentID() {
		return agentID;
	}
	
}

protected 접근 제어자

  • 상위 클래스에 선언된 private 멤버 변수는 하위 클래스에서 접근할 수 없음
  • 외부 클래스는 접근할 수 없지만, 하위 클래스는 접근 할 수 있도록 protected접근 제어자를 사용

Customer.java get,setter 추가

CustomerTest.java

public class CustomerTest {

	public static void main(String[] args) {
		
		Customer customerLee = new Customer();
		customerLee.setCustomerName("이순신");
		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());
	}

}


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

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

  • 하위 클래스를 생성하면 상위 클래스가 먼저 생성 됨
  • new VIPCustomer()를 호출하면 Customer()가 먼저 호출 됨
  • 클래스가 상속 받은 경우 하위클래스의 생성자에서는 반드시 상위 클래스의 생성자를 호출 함

각 객체를 불러올 때 출력될 문구를 넣어줬다
CustomerTest.java

VIPCustomer의 생성자가 호출될 때 어떻게 불려오는지 확인하기위해 Customer 주석처리

출력결과

상위 클래스인 Customer를 먼저 불러오는것을 확인할 수 있다.

super() 키워드

  • 하위 클래스에서 가지는 상위 클래스에 대한 참조 값
  • super()는 상위 클래스의 기본 생성자를 호출함
  • 하위 클래스에서 명시적으로 상위 클래스의 생성자를 호출하지 않으면 super()가 호출 됨
    ( 이 때 반드시 상위 클래스의 기본생성자가 존재 해야함 )
  • 상위 클래스의 기본 생성자가 없는 경우( 다른 생성자가 있는 경우 ) 하위 클래스에서는 생성자에서는 super를 이용하여 명시적으로 상위 클래스의 생성자를 호출 함
  • super()는 생성된 상위 클래스 인스턴스의 참조 값을 가지므로 super를 이용하여 상위 캘래스의 메서드나 멤버 변수에 접근할 수 있음

디폴트 생성자를 없애고 매개변수가 있는 생성자를 추가 / super를 이용하여 상위 클래스의 생성자 명시적으로 호출
출력 결과

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

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

형 변환 ( 업캐스팅 )

  • 상위 클래스로 변수를 선언하고 하위 클래스의 생성자로 인스턴스를 생성
    Customer customerLee = new VIPCustomer();
  • 상위 클래스 타입의 변수에 하위 클래스 변수가 대입
    VIPCustomer vCustomer = new VIPCustomer();
    add Customer(vCustomer);
    int addCustomer(Customer customer){

    }
  • 하위 클래스는 상위 클래스의 타입을 내포하고 있으므로 상위 클래스로의 묵시적 형 변환이 가능함
  • 상송 관계에서 모든 하위 클래스는 상위 클래스로 형 변환(업 캐스팅)이 됨(그 역은 성립되지않음)

형 변환과 메모리

Customer vc = new VIPcustom(); 에서 vc가 가리키는것은?

VIPCustomer() 생성자에 의해 VIPCustomer 클래스의 모든 멤버 변수에 대한 메모리는 생성되었지만,
변수 타입이 Customer이므로 실제 접근 가능한 변수나 메서드는 Customer의 변수와 메서드이다

vc의 타입은 Customer 이지만 인스턴스가 VIP로 생성이 되었다.

vc가 Customer의 타입으로 형변환이 됐기 때문에 Customer의 멤버 변수만 사용이 가능하다 ( 자기 타입 멤버변수만 사용가능)

클래스의 계층구조가 여러 단계인 경우

  • Human은 내부적으로 Promate와 mammal의 타입을 모두 내포하고 있음
    Primate pHumman = new Humman();
    Mammal mHumman = new Humman();

4. 메서드 재정의 하기 ( overring )

하위 클래스에서 메서드 재정의 하기

  • 오버라이딩 ( overriding ) : 상위클래스에 정의된 메서드의 구현 내용이 하위 클래스에서 구현할 내용과 맞지 않는 경우 하위 클래스에서 동일한 이름의 메서드를 재정의 할 수 있음
  • VIPCustomer 클래스의 calcPrice()는 할인율이 적용되지 않음
  • 재정의 하여 구현해야함

VIPCustomer.java

Override 내용 추가

@overriding 애노테이션 ( annotation )

  • 애노테이션은 원래 주석이라는 의미
  • 컴파일러에게 특별한 정보를 제공해주는 역할

  • @overriding 애노테이션은 재정의 된 메서드라는 의미로 선언부가 기존의 메서드와 다른 경우 에러가 남

형 변환과 오버라이딩 메서드 호출

  • Customer vc = new VIPCustomer();
    vc 변수의 타입은 Customer이지만 인스턴스의 타입은 VIPCustomer임
    자바에서는 항상 인스턴스의 메서드가 호출 됨 ( 가상 메서드의 원리 )
    자바의 모든 메서드는 가상 메서드 ( virtual method ) 임

5. 메서드 재정의와 가상 메서드의 원리

메서드는 어떻게 호출되고 실행이 되나?

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

	int num;
    
    void aaa() {
    	System.out.println("aaa() 호출");
    }
    
    public static void main(String[] args){
    	
        TestMethod a1 = new TestMethod();
        a1.aaa();
        
        TestMethod a2 = new TestMethod();
        a2.aaa();
    }

}

가상 메서드의 원리

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


6.  다형성과 다형성을 사용하는 이유

다형성 ( polymorphism ) 이란

  • 하나의 코드가 여러 자료형으로 구현되어 실행되는 것
  • 같은 코드에서 여러 다른 실행 결과가 나옴
  • 정보은익, 상속과 더불어 객체지향 프로그래밍의 가장 큰 특징 중 하나
  • 다형성을 잘 활용하면 유연, 확장성, 유지보수가 편리한 프로그램을 만들 수 있다.

다형성의 예

class Animal {
	
	public void move() {
		System.out.println("동물이 움직입니다.");
	}
}

class Human extends Animal {

	@Override
	public void move() {
		System.out.println("사람이 두 발로 걷습니다.");
	}
	
	public void readBook() {
		System.out.println("사람이 책을 읽습니다.");
	}
	
}

class Tiger extends Animal {

	@Override
	public void move() {
		System.out.println("호랑이가 네 발로 뜁니다");
	}
	
	public void hunting() {
		System.out.println("호랑이가 사냥을 합니다.");
	}
	
}

class Eagle extends Animal {

	@Override
	public void move() {
		System.out.println("독수리가 하늘을 납니다");
	}
	
	public void flying() {
		System.out.println("독수리가 양 날개를 쭉 펴고 날아다닙니다");
	}
	
}

public class AnimalTest {

	public static void main(String[] args) {

		Animal hAnimal = new Human();
		Animal tAnimal = new Tiger();
		Animal eAnimal = new Eagle();
		
		AnimalTest test = new AnimalTest();
		test.moveAnimal(hAnimal);
		test.moveAnimal(tAnimal);
		test.moveAnimal(eAnimal);
	}
	
	public void moveAnimal(Animal animal) {
		animal.move();
	}

}

다형성을 사용하는 이유

  • 다른 동물을 추가하는 경우
  • 상속과 메서드 재정의를 활용하여 확장성 있는 프로그램을 만들 수 있음
  • 그렇지 않는 경우 if - else if 문이 구현되고 코드의 유지 보수가 어려워짐

  • 상위 클래스에서는 공통적인 부분을 제공하고 하위 클래스에서는 각 클래스에 맞는 기능 구현
  • 여러 클래스를 하나의 타입(상위 클래스)으로 핸들링 할 수 있음

다형성을 활용한 멤버십 프로그램 확장

  • 일반 고객과 VIP 고객 중간 멤버십 만들기
    고객이 늘어 일반 고객보다는 많이 구매하고 VIP보다는 적게 구매하는 고객에게도 해택을 주기로 했다.
    GOLD 고객 등급을 만들고 혜택은 다음과 같다
    1. 제품을 살때는 10프로를 할인해준다
    2. 보너스 포인트는 2%를 적립해준다
import java.util.ArrayList;

public class CustomerTest {

	public static void main(String[] args) {
		
		ArrayList<Customer> customerList = new ArrayList<>();
		
		Customer customerT = new Customer(10010, "Tomas");
		Customer customerJ = new Customer(10020, "James");
		Customer customerE = new GoldCustomer(10030, "Edward");
		Customer customerP = new GoldCustomer(10040, "Percy");
		Customer customerK = new VIPCustomer(10050, "Kim");
		
		customerList.add(customerT);
		customerList.add(customerJ);
		customerList.add(customerE);
		customerList.add(customerP);
		customerList.add(customerK);
		
//		for ( Customer customer : customerList ) {
//			System.out.println(customer.showCustomerInfo());
//		}
		
		int price = 10000;
		for (Customer customer : customerList) {
			
			int cost = customer.calcPrice(price);
			System.out.println(customer.getCustomerName()+"님이 "+ cost + "원 지불했습니다.");
			System.out.println(customer.getCustomerName()+"님의 현재 보너스 포인트는 " + customer.bonusPoint + "입니다.");
		}
	}
	
}


7. 상속을 언제 사용하는가 ?

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

  • 일반적인 개념과 구체적인 개념과의 관계
  • 상위 클래스 : 하위 클래스보다 일반적인 개념 ( Ex: Employee )
  • 하위 클래스 : 상위 클래스보다 구체적인 개념들이 더해짐 ( Ex : Engineer, Manager .~ )
  • 상속은 클래스간의 결합도가 높은 설계
  • 상위 클래스의 수정이 많은 하위 클래스에 영향을 미칠 수 있음
  • 계층구조가 복잡하거나 hierachy가 높으면 좋지 않다

HAS-A 관계 ( composition )

  • 클래스가 다른 클래스를 포함하는 관계 ( 변수로 선언 )
  • 코드 재사요의 가장 일반적인 방법
  • Student가 Subject를 포함하는
  • Library를 구현할 때 ArrayList생성하여 사용
  • 상속하지 않음
반응형