자바 상속

KangMyungJoe·2020년 12월 23일
1

java_study

목록 보기
5/8
post-thumbnail

백기선님과 함께하는 Java-Study 6주차

6주차 과제: 자바 상속
목표: 자바의 상속에 대해 학습하기

  • 자바 상속의 특징
  • super 키워드
  • 메소드 오버라이딩
  • 다이나믹 메소드 디스패치 (Dynamic Method Dispatch)
  • 추상 클래스
  • final 키워드
  • Object 클래스

1. 자바 상속의 특징

자바 상속의 특징에 대해 공부하기 전에, 상속이란 무엇인지 간단히 살펴보자.

상속이란?

  • 자식 클래스가 부모 클래스의 내용을 고스란히 획득하는 것
  • Object-Oriented-Programming의 주요 개념 중 하나
  • 상속 해주는 클래스 -> 부모 클래스 (Super Class)
  • 상속 받는 클래스 -> 자식 클래스 (Sub Class)


그림으로 나타내면 위와 같이 나타낼 수 있다. 부모의 필드와 메소드를 상속받은 자식 클래스는 받은 그대로 사용할 수도 있고, override 하여 사용할 수도 있다.

상속에도 몇가지 종류가 있다.

이 외,

  • 부모 클래스 2개로부터 상속을 받는 다중 상속
  • 하이브리드 상속 등이 있다.

자바 상속의 특징은 다음과 같다.

  • 다중 상속이 불가능
  • 모든 Class는 Object Class의 Sub Class

다중 상속이 불가능 한 이유로, 2개의 부모 클래스의 중복 가능성이 있다.

만약, 2개의 부모 클래스에 동일한 이름의 필드나 메소드가 있으면?

  • 어떤 부모 클래스의 필드 or 메소드를 상속받아야 하는가?
  • 어떤 부모 클래스에 어떻게 접근해야 하는가?

와 같은 문제가 발생할 수 있다.


2. super 키워드

super 키워드는 자식 클래스에서 부모 클래스를 가리킬 때 사용하는 키워드다. 주로 부모 클래스의 필드에 접근, 메소드를 호출할 때 사용한다.

다음과 같은 코드가 있을 때, Son 클래스의 printMotehrAge 메소드에서 super.motherAge의 형식으로 Mother 클래스에 있는 필드에 접근할 수 있다.

class Mother {
    int motherAge = 50;

    public Mother(){
        System.out.println("welcome to super class");
    }
}

class Son extends Mother{
    int sonAge = 23;

    public Son(){
        System.out.println("welcome to sub class");
    }

    public void printMotherAge() {
        System.out.println("Mother's Age : " + super.motherAge);
    }
}

public class Study_6 {
    public static void main(String[] args) {
        Mother mother = new Mother();
        System.out.println("~~~~~~~~~~~~~~~~~~");
        Son son = new Son();
        son.printMotherAge();
    }
}

// 결과 값 :

welcome to super class
~~~~~~~~~~~~~~~~~~
welcome to super class
welcome to sub class
Mother's Age : 50

또한, 결과 값에서 확인할 수 있는 사실이 하나 더 있다.

  • 자식 클래스의 생성시 부모 클래스의 생성자가 함께 호출됨

코드를 다시 살펴봐도, Mother 클래스에 대한 객체를 생성할 때를 제외하고 "welcome to super class"가 출력되는 부분이 존재하지 않지만, 결과 값에는 2번 출력되는 모습을 확인할 수 있다.

그 이유로는, 컴파일러가 자식 클래스의 생성자 함수에 부모 클래스 생성자 함수인 super()를 자동으로 끼워넣어 주기 때문이다.

  • 자식 클래스가 생성되면 부모 클래스의 생성자가 자동으로 실행된다!
    (자식 생성자보다 일찍 실행)

자식 클래스에서 부모 클래스에 대해 super super() 2가지가 존재한다.
super: 부모 클래스의 필드나 메소드에 접근 가능
super(): 부모 클래스의 생성자 실행


3. 메소드 오버라이딩

메소드 오버라이딩이란,

부모 클래스로부터 물려받은 메소드를 재정의 하여 사용하는 방식

즉, 자식 클래스의 입맛에 맞게끔 메소드를 변경한다고 이해할 수 있다.

아래 코드로 이해해보자.

class Mother {
    int motherAge = 50;

//    public Mother(){
//        System.out.println("welcome to super class");
//    }

    public void speak() {
        System.out.println("I am Mother!");
    }
}

class Son extends Mother{
    int sonAge = 23;

//    public Son(){
//        System.out.println("welcome to sub class");
//    }

    @Override
    public void speak() {
        System.out.println("I am Son!");
    }

}
public class Study_6 {
    public static void main(String[] args) {
        Mother mother = new Mother();
        Son son = new Son();
        
        mother.speak();
        son.speak();

    }
}

// 결과 값 :

I am Mother!
I am Son!

깔끔한 결과 값을 위해 생성자는 모두 주석 처리했다.

Mother 클래스를 상속 받은 Son 클래스는 speak() 메소드 또한 상속받았다.
하지만 Son 클래스는 speak 할 때, '나는 엄마다!' 라고 할 수 없기 때문에, 자신에게 맞는 '나는 아들이다!'로 메소드를 변경해 사용하려고 한다.

이렇게 상속받은 speak() 메소드를 다시 재정의 하여 사용하는 방법을
메소드 오버라이딩 이라고 한다.


메소드 오버라이딩에는 몇가지 규칙이 존재한다.

  • 상속받은 메소드와 동일한 이름, 매개 변수들, 리턴 타입을 가져야 한다.
  • 상속받은 메소드보다 강한 접근 제한을 하지 못한다.
  • 새로운 예외 처리를 추가할 수 없다. (throw)

위와 같은 규칙들을 지켜야 정확한 메소드 오버라이딩을 진행할 수 있다.


4. 다이나믹 메소드 디스패치 (Dynamic Method Dispatch)

동적 메소드 디스패치란, 컴파일 타임이 아닌, 런타임에 오버라이딩 된 메서드에 대한 호출이 이루어지는 방식으로, 대표적으로 staticdynamic이 있다.

Static Method Dispatch란, 앞서 말한 3장에서 사용하는 방법으로 메소드의 호출이 컴파일 타임에 결정된다. private 접근 제어자, static, final 키워드를 사용한 메소드들은 정적 메소드 디스패치로써 호출된다.

다음 코드는 정적 메소드 디스패치의 예다.

class Mother {
    int motherAge = 50;

    public void printAge() {
        System.out.println("I'm " + motherAge);
    }
}

class Son extends Mother {
    int sonAge = 23;

    @Override
    public void printAge() {
        System.out.println("I'm " + sonAge);
    }
}
public class Study_6 {
    public static void main(String[] args) {
        Mother mother = new Mother();
        Son son = new Son();

        mother.printAge();
        son.printAge();
    }
}


// 결과 값 :

I'm 50
I'm 23

Dynamic Method Dispatch란, 앞서 설명한 것 처럼, 컴파일 타임이 아닌 런타임에 실행 방식이 정해진다. 이는 다형성을 위한 핵심 속성 중 하나다.

같은 타입을 가진 클래스에서 다른 내용을 갖고 있는 객체들이 어떤 메소드를 어떤 방식으로 실행해야 하는 지를 알게 된다.

즉, 컴파일러가 어떤 메소드가 먼저 실행될 지 예측할 수 없다.

다음은 동적 메소드 디스패치의 예다.

class Animal {
    public void speak() {
        System.out.println("Animal class");
    }
}

class Dog extends Animal {
    @Override
    public void speak() {
        System.out.println("wak! wak!");
    }
}

class Cat extends Animal {
    @Override
    public void speak() {
        System.out.println("meow! meow!");
    }
}

public class Study_6 {
    public static void main(String[] args) {
        Animal dog = new Dog();
        Animal cat = new Cat();

        dog.speak();
        cat.speak();
    }
}


// 결과 값 :

wak! wak!
meow! meow!

5. 추상 클래스

추상 클래스란, 실질적으로 동작하는 클래스에서 가져야 할 필드와 메소드를 정의해놓은 클래스다. 주로 클래스들의 필드와 메소드를 통일시키고자 할 때 사용한다.

추상 클래스에서 정의하는 추상 메소드는 body 부분을 가질 수 없다.

즉, 추상 클래스의 추상 메소드는 상속 받는 클래스에서 재정의 해야한다!

아래 코드를 보며 이해하자.

public abstract class Animal {   // 정말 단순한 추상 클래스
    public abstract void bark();   //  추상 메소드
}

class Dog extends Animal{  // 추상 클래스를 상속받음

    @Override
    public void bark() {   // 추상 클래스 오버라이딩
        System.out.println("wak! wak!");
    }
}

class Cat extends Animal {   // 추상 클래스를 상속받음

    @Override
    public void bark() {   // 추상 클래스 오버라이딩
        System.out.println("meow! meow!");
    }
}

public class Study_6 {
    public static void main(String[] args) {
        Dog dog = new Dog();
        Cat cat = new Cat();

        dog.bark();
        cat.bark();

    }
}


// 결과 값 : 

wak! wak!
meow! meow!

위 코드는 추상 클래스로 작성한 Animal 클래스를 Dog, Cat 클래스가 상속받고, 추상 메소드로 선언된 bark()를 재정의 하여 결과 값을 반환했다.

추상 클래스는 공통적인 부분을 가져야하는 구조에서 주로 사용된다.


6. final 키워드

final 키워드란, 선언한 클래스, 필드, 메소드가 더 이상 수정하지 못하는 상태를 뜻하는 키워드로 상속이 불가능하기 때문에 항상 자식 클래스로써 존재한다.

예를 들어 아래와 같은 코드가 있을 때, 작성한 코드 2줄은 모두 에러가 발생한다.

public class Last {
    final int MONTH;   // 필드에는 초기화 값이 항상 존재해야 함
    
    final void returnMonth();  // 메소드에는 body가 항상 존재해야 함
}

변수에 값을 넣고, 메소드에도 body 부분을 작성했다.

final 키워드를 사용한 메소드를 오버라이딩 해보려고 한다면 어떤 결과가 나올까?

public class Last {
    final int MONTH = 10;   // 필드에는 초기화 값이 항상 존재해야 함
    
    final void returnMonth() {   // 메소드에는 body가 항상 존재해야 함
    	System.out.println(MONTH); 
    }  
}

class Present extends Last {   // Last 클래스의 returnMonth() 메소드 오버라이딩
    @Override
    void returnMonth() {

    }
}
public class Study_6 {
    public static void main(String[] args) {
        Present present = new Present();
    }

}


// 결과 값 :

java: returnMonth() in inheritance_6_4.Present cannot override returnMonth() 
in inheritance_6_4.Last overridden method is final

바로 오류가 발생한다. 오류의 내용은 다음과 같다.

  • final 키워드를 이용해 선언된 메소드를 오버라이딩 할 수 없다.

final 키워드를 사용한 변수의 이름에도 규칙이 존재한다.

  • 변수 이름의 모든 문자는 대문자여야 한다.
  • 변수 이름에 _(언더바)를 선택적으로 이용할 수 있다.

7. Object 클래스

Object 클래스란, 모든 클래스의 부모 클래스, 최상위 클래스다. Object 클래스는 필드를 가지지 않으며, 총 11개의 메소드로 이루어져 있다.

메소드는 다음과 같다.

출처

Object 클래스는 컴파일 타임에 컴파일러가 자동으로 extends 할 수 있게 한다.


참고1
참고2

profile
소통을 잘하는 개발자가 되고 싶습니다.

0개의 댓글