본문 바로가기
Java

[Java] 상속

by Hindsight.. 2021. 2. 21.

백기선님이 진행하시는 Live Study 6주차 과제입니다.

github.com/whiteship/live-study/issues/6

 

6주차 과제: 상속 · Issue #6 · whiteship/live-study

목표 자바의 상속에 대해 학습하세요. 학습할 것 (필수) 자바 상속의 특징 super 키워드 메소드 오버라이딩 다이나믹 메소드 디스패치 (Dynamic Method Dispatch) 추상 클래스 final 키워드 Object 클래스 마

github.com

 

 

1. 자바 상속의 특징

상속(Inheritance) : 자식 클래스가 부모클래스의 모든 것을 물려받는 것.

public class inheriment {

    public static class Animal{
        String name;
        public void setName(String name){
            this.name = name;
        }
        public void go(){
            System.out.println(name + "gogogo");
        }
    }

    public static class Dog extends Animal{
        public void bark(){
            System.out.println("barkarkakrark");
        }
    }

    public static void main(String[] args){
//      #Case 1
        Animal ani = new Animal();
        ani.setName("ani1");
        ani.go();
//        ani.bark(); // cannot find symbol: method bark()
        
//      #Case 2
        Animal ani2 = new Dog();
        ani2.setName("ani2");
        ani2.go();
//      ani2.bark(); // cannot find symbol: method bark()
        
//      #Case 3
//      Dog ani3 = new Animal(); // Animal cannot be converted to Dog

//      #Case 4
        Dog ani4 = new Dog();
        ani4.setName("ani4");
        ani4.go();
        ani4.bark();

    }
}

실행가능한 전체 코드

부모 클래스인 Animal 과 자식 클래스인 Dog 의 관계와 각 케이스 별로 나누어보았다.

 

 

public static class Animal{
    String name;
    public void setName(String name){
        this.name = name;
    }
    public void go(){
        System.out.println(name + "gogogo");
    }
}

public static class Dog extends Animal{
    public void bark(){
        System.out.println("barkarkakrark");
    }
}

부모 클래스인 Animal , name 변수와 setName, go 함수를 갖고 있다.

그리고 자식 클래스인 Dog, bark라는 함수를 갖고있다.

 

이제 각 케이스별로 상세히 살펴본다.

//      #Case 1
        Animal ani = new Animal();
        ani.setName("ani1");
        ani.go();
//      ani.bark(); // cannot find symbol: method bark()

Animal 변수형에 Animal 객체로 초기화한 경우이다.

당연하게도 Animal 객체에 선언된 함수만을 사용할 수 있었고, bark 함수는 cannot find symbol 함수가 출력된다.

 

//      #Case 2
        Animal ani2 = new Dog();
        ani2.setName("ani2");
        ani2.go();
//      ani2.bark(); // cannot find symbol: method bark()

Animal 변수형에 Dog로 초기화 한 경우

Dog로 초기화 했지만, 변수 자체가 Animal 이기 때문에 bark는 사용할 수 없었다.

 

//      #Case 3
//      Dog ani3 = new Animal(); // Animal cannot be converted to Dog

이 경우는 컴파일 조차 불가능했다. 자식 변수형에 부모 클래스로 초기화 하는것은 불가능하다.

 

//      #Case 4
        Dog ani4 = new Dog();
        ani4.setName("ani4");
        ani4.go();
        ani4.bark();

당연하게도 모두 가능한 케이스

Dog 변수형에 Dog를 초기화 했다. Dog, Animal의 모든 변수와 함수가 사용 가능했다.

 

정리)

변수 형은 그릇이다. 선언된 그릇의 크기에 따라 담을 수 있는 양과 모양이 달라진다고 생각했다.

모양이 아예 다른 내용물은 담지 못하고, 모양이 비슷해도 크기가 다르면 모두 담을 수 없었다.

 

2. super 키워드

 

먼저, super와 super()는 살짝 다르다. super의 용도를 본 후 super()로 넘어가자.

public class SuperPrac {

    public static class Animal{
        String name = "Animal";
    }

    public static class Dog extends Animal{
        String name = "Dog";
        public void sayName(){
            System.out.println(this.name); // Dog, Dog의 name
            System.out.println(super.name); // Animal, Animal의 name
        }
    }

    public static void main(String[] args){
        Dog dog = new Dog();
        dog.sayName();
    }
}

super 는 이렇게 부모 클래스의 멤버변수에 접근할 수 있는 키워드이다

반면 this는 본인 클래스의 멤버변수에만 접근 할 수 있는 키워드이다.

 

super() 메소드

 

부모 클래스의 생성자를 호출할 때 사용된다.

 

public static class Animal{
    String name = "Animal";
    public Animal(String name){
        this.name = name;
    }
}

public static class Dog extends Animal{
    String name = "Dog";
    public Dog(){
        super("Not Animal");
    }
    public void sayName(){
//      super("Not Animal");
        System.out.println(this.name); // Dog
        System.out.println(super.name); // Not Animal
    }
}

super() 메소드는 부모 클래스의 생성자를 호출 할 수 있는 함수이다. 

본래 생성자가 선언되어있지 않으면, JAVA 컴파일러는 기본 생성자인, super()를 넣어 생성한다.

 

그런데, 부모 클래스에서 생성자를 커스텀해서 선언해줬다면 자식 클래스 또한 거기에 맞춰 커스텀으로 선언해주어야 한다는 것을 기억하자.

 

3. 메소드 오버라이딩

public class OverrideExp {

    public static class Animal{
        public void say(){
            System.out.println("hiyo");
        }
    }

    public static class Dog extends Animal{
        public void say(){
            System.out.println("barkrkrkrk");
        }
    }

    public static void main(String[] args){

        // CASE 1
        Animal ani = new Animal();
        ani.say(); // hiyo

        // CASE 2
        Dog ani2 = new Dog();
        ani2.say(); // barkrkrkrk

        // CASE 3
        Animal ani3 = new Dog();
        ani3.say(); // barrkrkrk

    }
}

Animal 과 Dog에 동시에 선언된 say 함수는 Animal 에서는 "hiyo"를, Dog 에서는 "barkrkrkrk"를 출력한다.

케이스들의 결과를 보면,

CASE 1에서는 Animal에 선언된 대로 hiyo를, 

CASE 2에서는 Dog에 선언된 대로 barkrkrkrk를,

CASE 3에서는 예상 외로 barkrkrkrk를 출력했다.

Animal의 함수과 변수들만 사용할 수 있는줄 알았지만, 자식의 함수더라도, 오버라이딩 된 메소드들은 그대로 변수형에 들어간다는 것을 알 수 있었다.

 

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

public class DynamicDispatch {
    public static class Animal{
        public void say(){
            System.out.println("I'm Animal");
        }
    }

    public static class Dog extends Animal{
        public void say(){
            System.out.println("I'm Dog");
        }
    }

    public static class Cat extends Dog{
        public void say(){
            System.out.println("I'm Cat");
        }
    }

    public static void main(String[] args){
        Animal animal = new Animal();
        animal.say(); // I'm Animal
        animal = new Dog();
        animal.say(); // I'm Dog
        animal = new Cat();
        animal.say(); // I'm Cat
    }
}

다이나믹 메소드 디스패치 => 메소드가 동적으로 붙여진다는 의미이다.

가장 upcasting된 메소드가 선택되는데, 가장 하위 즉, 가장 자식 클래스의 메소드가 선택된다는 뜻이다.

 

예제의 경우 say 메소드가 3개의 클래스에 모두 선언되어있고, Dog는 Animal의 상속을 받고, Cat은 Dog의 상속을 받는다.

main 내에서는 각각 Animal, Dog, Cat으로 초기화 한 세가지의 케이스가 있고, 모두 가장 자식클래스의 메소드가 실행되는 것을 알 수 있다.

 

5. 추상 클래스

Abstract 추상적인, 이라는 뜻의 클래스이다. 일반 클래스 처럼 변수 또는 함수를 선언할 수 있지만, abstaract를 붙여 형태만을 붙일 수 있다. 일반 변수 또는 함수를 선언할 수 없다는 것이 인터페이스와의 차이점이다.

 

public class AbstractPrac {
    
    public static abstract class Animal{
        String name;
        public void myType(){
            System.out.println("My Type is Animal");
        }
        public abstract void say();
    } 
    
    public static class Dog extends Animal{

        @Override
        public void say() {
            System.out.println("barkrkrk");
        }
    }
    
    public static void main(String[] args){
        Dog dog = new Dog();
        dog.myType();
        dog.say();
    }
}

Animal 클래스의 say함수에 abstract 키워드가 붙어 있다. 그리고 Dog에는 위에서 보았던 Override가 붙어 say()가 정의 되어있다.

abstract 클래스를 상속받은 자식 클래스는 강제로 abstract 키워드가 붙은 함수를 Override 해야한다.

 

따라서 abstract  클래스는 미리 정의 해놓은 틀 정도로 생각하면 좋다. 미리 정의해놓은 함수들을 포함해 자식 클래스들에서 필수적으로 구현해야할 함수들에 대한 틀을 정해놓아 자식 클래스를 원하는 타입으로 유도할 수 있겠다.

 

6. final 키워드

final로 선언한 클래스를 상속하려면 "Cannot inherit from final 'Animal'" 오류가 출력된다. final로 선언한 클래스는 상속할 수 없다!

 

final로 선언한 메소드 또한, 오버라이드 할 수 없다!

 

하지만, 오버로딩은 가능했다.

final로 선언된 변수 또한 재할당, 수정이 불가능 하다!

 

정리) final 키워드는 변경 및 재선언, 오버라이드가 불가능 하므로, 상수 및 변경 불가능한 함수를 만들고 싶을때 사용하자!

 

7. Object 클래스

Object 클래스는 오라클의 공식문서를 만큼 깔끔하고 명료한 설명이 없어 생략..

docs.oracle.com/javase/7/docs/api/java/lang/Object.html

 

Object (Java Platform SE 7 )

Called by the garbage collector on an object when garbage collection determines that there are no more references to the object. A subclass overrides the finalize method to dispose of system resources or to perform other cleanup. The general contract of fi

docs.oracle.com

 

'Java' 카테고리의 다른 글

[Java] 인터페이스  (0) 2021.03.04
[Java] 패키지  (0) 2021.02.27
[Java] 클래스  (0) 2021.01.04
[Java] 제어문, Queue, Stack, LinkedList  (0) 2020.12.23
[Java] 연산자  (0) 2020.12.20