본문 바로가기

Java

[WS live-study] 6주차: 상속

학습할 것

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

상속

상속이란?

자식클래스가 상속받을 부모클래스를 선택하여 받는 것으로 상속받은 부모클래스의 필드와 메소드를 사용할 수 있다.

상속하는 이유와 대상

상속하는 이유는 이미 마련되어 있는 클래스를 재사용할 수 있다. 따라서 개발 시간을 줄여주고, 중복된 코드를 줄일 수 있기 때문이다. 

모든 대상이 상속 가능한 것은 아니다. 아래의 경우를 제외한 대상은 상속 가능하다.

  1. 부모 클래스의 접근제어자가 default인 경우
  2. 부모 클래스의 필드 또는 메서드의 접근제어자가 private인 경우
  3. 다른 패키지의 부모 클래스의 필드 또는 메서드의 접근제어자가 default인 경우

상속의 특징

  1. 다중 상속은 허용이 안된다. 1개만 상속받을 수 있음.
  2. 부모 클래스는 여러 자식클래스를 상속할 수 있다.  (현실에서 자식은 여러 부모를 둘 수 없지만 부모는 여러 자식을 둘 수 있는 것을 생각해보면 이해가 쉽다.)
  3. 모든 클래스의 최상위 클래스는 Object 클래스이다.

상속 방법 (선언)

접근제어자 class 자식클래스명 extends 부모클래스명 { ... }
class Child extends Parent {
    String hobby;
    String school;

    public Child(String name, int age, String address, String hobby, String school) {
        super(name, age, address);
        this.hobby = hobby;
        this.school = school;
    }
}

위의 예와 같이 extends 후에 부모 클래스명을 작성하면 된다. 2개 이상 작성하면 컴파일 에러가 나니 주의해야 한다.

super 키워드

super 란?

전에 설명했던 this를 생각하면 super 키워드의 이해가 쉽다. this는 클래스 내에 인스턴스 자신을 가리키는 키워드였다. super는 자신의 부모클래스를 가리키는 것이다. 부모 클래스로부터 상속받은 변수와 멤버변수, 지역변수의 이름이 같을 때 super, this 키워드를 사용하면 구분할 수 있다.

 

super를 이용한 부모클래스의 필드와 메서드 호출

class Parent {
    String name;
    int age;
}
class Child extends Parent {
    String hobby;
    String school;

    public Child(String hobby, String school) {
        this.hobby = hobby;
        this.school = school;
    }

    public void changeMyInfo(String name, int age) {
        super.name = name;
        super.age = age;
    }
}

 

위의 예를 보면 부모 클래스를 선언하고 자식클래스가 상속을 받았다. 부모 클래스의 필드인 name, age를 사용하려면 super를 이용하여 필드를 가리킬 수 있다. 메서드 호출도 super.메소드명(인자1, 인자2, ...)  형식으로 한다면 호출된다.

 

 

super()를 이용한 부모클래스의 생성자 호출

super()는 부모클래스의 생성자를 호출하는 것이다. 그리고 모든 클래스에서는 자신의 생성자의 첫번째 줄에 부모클래스의 생성자를 호출해야 한다. 그런데 위의 예에서도 볼 수 있듯이 부모 클래스의 생성자를 호출하는 super()가 자식클래스 내에서 보이지 않는다. 이유는 우리가 호출하지 않아도 컴파일러가 자동으로 클래스의 생성자에서 super()를 추가하여 호출시켜준다.

 

class Parent {
    String name;
    int age;

    Parent(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

하지만 위의 예와 같이 부모 클래스의 생성자가 기본생성자가 아닌 경우에 자식클래스에서 부모 클래스의 생성자를 호출하지 않은 경우에는 컴파일 에러가 난다.

class Child extends Parent {
    String hobby;
    String school;

    public Child(String hobby, String school) {  // --> 컴파일 에러!
        this.hobby = hobby;
        this.school = school;
    }
}

따라서 아래와 같이 호출을 해주면 된다.

class Child extends Parent {
    String hobby;
    String school;

    public Child(String hobby, String school) {
        super("child1", 12);
        this.hobby = hobby;
        this.school = school;
    }
}

메소드 오버라이딩 (method overriding)

오버라이딩(overriding) 이란?

부모클래스의 이미 정의된 메서드를 자식클래스가 자신에 맞게 다시 정의하는 것이다.

 

오버라이딩의 조건

  1. 기존메서드와 선언부가 같아야 한다.
  2. 기존메서드의 접근 제어자보다 더 좁은 범위의 접근 제어자로 변경할 수 없다.
  3. 기존메서드보다 더 많은 수의 예외를 선언할 수 없다.
class Parent {
    String name;
    int age;

    Parent(String name, int age) {
        this.name = name;
        this.age = age;
    }

    void methodA() {
        System.out.println("부모 클래스의 메서드");
    }
}
class Child extends Parent {
    String hobby;
    String school;

    public Child(String hobby, String school) {
        super("child1", 12);
        this.hobby = hobby;
        this.school = school;
    }

    @Override
    void methodA() {
        System.out.println("자식 클래스의 메서드");
    }
}

다이나믹 메소드 디스패치

다이나믹 메소드 디스패치란?

오버라이딩된 메서드는 기본적으로 부모 클래스 안에 숨겨져 있으며 자식클래스가 오버라이딩된 메서드 내에서 super 키워드를 사용하지 않는 한 호출되지 않는다. 이 메서드 호출 확인은 런타임에 발생하며 이를 다이나믹 메소드 디스패치라 한다.

public void move() {
      System.out.println("Animals can move");
   }
}

class Dog extends Animal {
   public void move() {
      System.out.println("Dogs can walk and run");
   }
}

public class TestDog {

   public static void main(String args[]) {
   
      Animal a = new Animal(); // Animal 참조변수와 객체
      Animal b = new Dog(); // Animal 참조변수지만 Dog 객체

      a.move(); // Animal class의 move() 메서드 실행
      b.move(); // Dog class의 move() 메서드 실행
   }
}

예제를 보면 b.move()는 Animal을 참조하지만 Dog의 move를 실행했다. 이는 컴파일 타임에는 참조 유형을 확인하지만 런타임에서 JVM은 객체 유형을 파악하고 해당 특정 객체에 속하는 메소드를 실행한다.

추상 클래스

추상 클래스란?

간단히 말하자면 실체가 없는 클래스이다. 실체 클래스(상속받는 클래스)들의 공통적인 성질을 뽑아 어느정도의 규격을 잡아 놓은 것을 말한다. 구체적으로 구현해놓지 않았기 때문에 추상메서드로는 객체를 생성할 수 없다.

 

그리고 이를 상속받은 실체클래스에서는 추상 클래스가 선언한 추상 메서드를 자신에 맞게 구현해야한다.

그럼 추상메서드란 무엇인가? 추상메서드는 몸통이 없는 메서드이다. 메서드는 선언부와 구현부로 나누어져 있다. 그런데 추상메서드는 구현부는 없고 선언부만 존재하며 접근제어자 뒤에 abstract 키워드가 붙어있다.

 

추상클래스를 선언하는 이유는 자식 클래스에서 추상메서드를 반드시 구현하도록 강요하기 위해서이다.

추상클래스를 상속받은 클래스는 추상메서드를 구현하지 않으면 컴파일 에러가 발생함으로서 내용을 구현해야한다.

 

class Marine {
    int x, y;
    void move(int x, int y)     { /*지정된 위치로 이동*/ }
    void stop()                 { /*현재 위치에 정지*/ }
    void stimPack()             { /*스팀팩을 사용한다.*/ }
}

class Tank {
    int x, y;
    void move(int x, int y)     { /*지정된 위치로 이동*/ }
    void stop()                 { /*현재 위치에 정지*/ }
    void changeMode()           { /*공격모드를 사용한다.*/ }
}

class Dropship {
    int x, y;
    void move(int x, int y)     { /*지정된 위치로 이동*/ }
    void stop()                 { /*현재 위치에 정지*/ }
    void load()                 { /*선택된 대상을 태운다.*/ }
    void unload()               { /*선택된 대상을 내린다..*/ }
}

예에서 보면 각각의 기능이 있는 가운데 공통적인 부분이 있다. 이부분을 뽑아낸다. 

abstract class Unit {
    int x, y;
    abstract void move(int x, int y);
    void stop()                 { /*현재 위치에 정지*/ }
}

class Marine extends Unit {
    void move(int x, int y)     { /*지정된 위치로 이동*/ }
    void stimPack()             { /*스팀팩을 사용한다.*/ }
}

class Tank extends Unit {
    void move(int x, int y)     { /*지정된 위치로 이동*/ }
    void changeMode()           { /*공격모드를 사용한다.*/ }
}

class Dropship extends Unit {
    void move(int x, int y)     { /*지정된 위치로 이동*/ }
    void load()                 { /*선택된 대상을 태운다.*/ }
    void unload()               { /*선택된 대상을 내린다..*/ }
}

변수 x, y와 메소드 stop() 은 상속의 장점을 받아 중복된 코드를 줄여준다. 여기서 주목할 점은 move 메서드이다. 추상클래스 만들기 전 예제는 각각 따로 선언해서 구현했다. 이렇게 하면 실수로 구현해야할 기능을 빼먹을 수도 있고, 같은 기능인데 선언이 제각각일 수도 있다. 하지만 공통된 기능을 추상 메소드로 선언해놓으면 이 문제가 해결이 되고 통일성도 지켜지기 때문에 이해하기 쉬운코드가 된다.

final 키워드

final은 '변경할 수 없는' 이라는 의미를 가지고 있어 한번 정의내리면 다시 정의를 내릴 수 없으며, 거의 모든 대상에 적용가능하다.

 

  • final 클래스 - 확장될 수 없는 클래스. 따라서 이 클래스는 다른 클래스에게 상속하지 못한다.
  • final 메서드 - 변경될 수 없는 메서드. 이 메서드는 오버라이딩을 통해 내용을 재정의할 수없다.
  • final 멤버변수, 지역변수 - 값을 변경할 수 없는 상수가 된다.

Object

오브젝트란 java의 최상위 클래스이다. 모든 클래스가 Object 클래스를 상속받기 때문에 모든 클래스는 Object 기본 메서드를 바로 사용할 수 있다.

 

Object 메서드는 필드를 가지지 않으며, 총 11개의 메서드를 구성하고 있다.

 

http://www.tcpschool.com/java/java_api_object

 

 

 

 

 

 


정리하는데 도움되었던 곳들

'Java' 카테고리의 다른 글

[WS live-study] 8주차: 인터페이스  (0) 2021.02.13
[WS live-study] 7주차: 패키지  (0) 2021.02.07
[WS live-study] 5주차: 클래스  (0) 2021.02.03
[WS live-study] 4주차: 제어문  (0) 2021.02.02
[WS live-study] 3주차: 연산자  (0) 2021.02.01