728x90

상속 (extends)

  • 이미 구현된 클래스보다 더 구체적인 기능을 구현할 때 사용
  • protected 접근제어자를 이용하여 하위 클래스에서도 상위 클래스의 멤버 변수에 접근이 가능하도록 한다.
  • 하위 클래스에서 상위 클래스를 상속받으면 하위 클래스 생성자에서 먼저 상위 클래스 생성자를 호출한다.

 

상속을 이용한 멤버십 시나리오 구현

| 📄 Customer.java

/**
 * 일반 고객 클래스
 */
public class Customer {
    // protected : 하위 클래스 접근가능
    protected int customerId;
    protected String customerName;
    protected String customerGrade;
    int bonusPoint;
    double bonusRatio;

    public Customer()
    {
        customerGrade = "SILVER";
        bonusRatio = 0.01;
    }

    public int calcPrice(int price) {
        bonusPoint += price * bonusRatio;
        return price;
    }

    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;
    }

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

    }
}

| 📄 VIPCustomer.java

/**
 * VIP 고객 클래스
 */

public class VIPCustomer extends Customer{

    private int agentID;
    double salesRatio;

    public VIPCustomer() {
        customerGrade = "VIP";    //오류 발생
        bonusRatio = 0.05;
        salesRatio = 0.1;
    }

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

    public int getAgentID() {
        return agentID;
    }
}

| 📄 CustomerTest.java

public class CustomerTest {
    public static void main(String[] args) throws Exception {
        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());
    }
}

 


Super

  • 상위 클래스에 기본 생성자가 있는 경우
    상위 클래스의 생성자를 호출하는 명시적인 코드가 없으면 super()를 자동으로 넣어서 컴파일 해준다.
  • 상위 클래스에 기본 생성자가 없고 다른 생성자가 있는 경우
    하위 클래스에서는 생성자에 super를 이용하여 명시적으로 상위 클래스의 생성자를 호출해야한다.
  • private 멤버 변수의 경우에도 메모리 상에는 만들어지는데, 접근만 불가능하게 되는 것이다.

 


형 변환 (업캐스팅)

Customer customerLee = new VIPCustomer();

  • VIPCustomer의 경우에도 Customer를 내포하고 있기 때문에 가능하다.
  • VIPCustomer 인스턴스는 Customer 타입의 인스턴스로 대입될 수 있다.
VIPCustomer vCustomer = new VIPCustomer();
addCustomer(vCustomer);
int addCustomer(Customer customer){ ... }
  • 업캐스팅 진행 시, 인스턴스 자체는 하위 클래스의 인스턴스 일지 모르지만 (내부 메서드 및 변수는 모두 만들어진 상태) 그러나 상위 클래스로 형 변환을 해줬기때문에 하위 클래스의 메서드 및 변수는 사용이 불가능하다.
  • ⭐️ 생성은 다 되지만 상위 클래스 형태로 형 변환된 경우에는 접근 자체는 상위 클래스의 메서드만 가능!

오버라이딩

  • Customer vc = new VIPCustomer();
  • 이때 vc 변수의 타입은 Customer지만 인스턴스 타입은 VIPCustomer이다.
  • 자바에서는 항상 인스턴스의 메서드가 호출된다. (가상메서드의 원리)
  • 자바의 모든 메서드는 가상 메서드 !
    <-> (C++의 경우에는 virtual 키워드를 함께 사용해야함)

 

가상 메서드

  • 가상 메서드 테이블에서 해당 메서드에 대한 address를 가지고 있다.
  • 만약 오버라이딩된 메서드가 존재한다면 재정의 된 메서드에 대한 주소를 갖게 되고 재정의된 메서드를 가리킨다.
  • 각 클래스마다 오버라이딩된 메서드에 대해 address가 매핑된다.

 


다형성

  • 여러 클래스를 하나의 타입으로 핸들링할 수 있다.
  • 상속과 메서드 재정의를 활용하여 확장성있는 프로그램을 만들 수 있다.

 

상속

  • is-a 관계
    • 상속을 통해 클래스간 결합도를 높임
  • has-a 관계
    • 상속하지 않으며 일반적인 코드 재사용의 방법

 

다운 캐스팅

  • 업 캐스팅된 클래스를 다시 본래의 클래스로 돌아가게 한다.
  • 업 캐스팅은 묵시적(implicit)하게 발생하지만 다운 캐스팅은 명시적으로 이뤄진다.
  • *Object 클래스는 최상위클래스이기 때문에 어떤 클래스든 Object클래스로 반환될 수 있다.
Customer vc = new VIPCustomer();              //묵시적
VIPCustomer vCustomer = (VIPCustomer)vc;      //명시적
  • instanceof 를 이용하여 인스턴스의 타입을 체크한다.

 


추상 클래스(abstract class)

  • abstract class <-> concrete class
  • 메서드의 구현부 없이 선언만 있는 추상 메서드를 포함한 클래스를 말한다.
  • abstract 예약어를 사용한다.
  • 상속을 위한 클래스
  • abstract 로 선언된 클래스는 인스턴스화(new) 될 수 없다.
    • 모든 메서드가 구현된 상태여도 abstract로 선언된 상태면 인스턴스화 불가

 

  • 추상 클래스 사용 이유
    • 공통으로 사용할 클래스만 구현해놓고 하위 클래스들이 추상 메서드를 구현하도록 책임을 위임하기 위함

 

| 📄 Computer.java

public abstract class Computer {

    public abstract void display(); // 추상 메서드
    public abstract void typing();

    void turnOn() {
        System.out.println("전원을 켭니다.");
    }

    void turnOff() {
        System.out.println("전원을 끕니다.");
    }
}

 

| 📄 Desktop.java

public class Desktop extends Computer{

    @Override
    public void display() {
        System.out.println("Desktop display");
    }

    @Override
    public void typing() {
        System.out.println("Desktop typing");
    }

    @Override
    void turnOff() {
        System.out.println("Desktop turnOff");
    }
}
  • 추상 클래스를 상속 받아, 메서드 구현

 

| 📄 NoteBook.java

 public abstract class NoteBook extends Computer{

    @Override
    public void display() {
        System.out.println("NoteBook display");
    }
}
  • 메서드를 일부만 구현할 경우에는 클래스를 abstract로 선언한다.

 


템플릿 메서드 패턴

  • 프레임워크에서 많이 사용되는 패턴
  • 추상 메서드나 구현된 메서드를 활용하여 코드의 흐름을 정의한다.
    • final 로 선언하여 하위 클래스에서 재정의 할 수 없게끔 한다.
    • 클래스를 final 로 선언하면 상속을 못하게끔 할 수 있다.
  • 미리 시나리오를 정의 해놓고 시나리오에 따라 프로그램의 흐름이 결정된다.

Hook Method

public void run() {} // 구현부는 있지만 구현이 되어있지 않은 상태
  • 상위 클래스에서 구현부를 비워두고 하위 클래스에서 재정의해서 사용하는 메서드

 


인터페이스

  • 모든 메서드가 추상 메서드로 선언 abstract
  • 모든 변수는 상수로 선언 final
  • 클래스나 프로그램이 제공하는 기능을 명시적으로 선언한다.
    • 클라이언트와 서버간의 일종의 약속 (어떤 기능을 구현할 것 인지)

| 📄 Calc.java

public interface Calc {

    double PI = 3.14;
    int ERROR = -99999999;

    int add(int num1, int num2);
    int substract(int num1, int num2);
    int times(int num1, int num2);
    int divide(int num1, int num2);

}

| 📄 Calculator.java

public abstract class Calculator implements Calc{

    @Override
    public int add(int num1, int num2) {
        return num1 + num2;
    }

    @Override
    public int substract(int num1, int num2) {
        return num1 - num2;
    }
}

| 📄 CompleteCalc.java

public class CompleteCalc extends Calculator{

    @Override
    public int times(int num1, int num2) {
        return num1 * num2;
    }

    @Override
    public int divide(int num1, int num2) {
        if( num2 == 0 )
            return ERROR;
        else 
            return num1 / num2;
    }
}

| 📄 CalculatorTest.java

public class CalculatorTest {

    public static void main(String[] args) {
        Calc calc = new CompleteCalc();
        int num1 = 10;
        int num2 = 2;

        System.out.println(num1 + "+" + num2 + "=" + calc.add(num1, num2));
        System.out.println(num1 + "-" + num2 + "=" +calc.substract(num1, num2));
        System.out.println(num1 + "*" + num2 + "=" +calc.times(num1, num2));
        System.out.println(num1 + "/" + num2 + "=" +calc.divide(num1, num2));
    }
}
  • Calc calc = new CompleteCalc();
    • CompleteCalc는 Calculator를 상속받았지만 Calculator가 Calc를 구현했으므로 Calc로 형 변환이 가능하다!
728x90

'🧑‍💻 Language > Java' 카테고리의 다른 글

[Java] 객체 지향과 디자인 패턴  (0) 2022.05.09
[Java] 자료 구조  (0) 2022.05.03
[Java] 객체 지향 프로그래밍(2)  (0) 2022.04.19
[Java] 객체 지향 프로그래밍 (1)  (0) 2022.04.07
[Java] Basic Java (2)  (0) 2022.04.04