java week6, 상속


목차



자바 상속의 특징


상속

상속이란 상위클래스에서 정의한 필드와 메서드를 하위클래스도 동일하게 사용할 수 있게 물려받는 것이다.

상속을 사용하는 이유

코드를 재사용하기에 편하고 클래스 간 계층구조를 분류하고 관리하기 쉬어진다.

게임캐릭터를 예를 들어보자

Unit.java

public class Unit {

    int hp;

    int attackPoint;

    public Unit(int hp, int attackPoint) {
        this.hp = hp;
        this.attackPoint = attackPoint;
    }

    public void attack(Unit unit) {
        unit.attackedBy(this);
    }

    public void attackedBy(Unit unit) {
        this.hp -= unit.attackPoint;
    }

}

Hero.java

public class Hero extends Unit{

    int level;

    public Hero(int hp, int attackPoint, int level) {
        super(hp, attackPoint);
        this.level = level;
    }

}

Villain.java

public class Villain extends Unit {

    int defensePoint;

    public Villain(int hp, int attackPoint, int defensePoint) {
        super(hp, attackPoint);
        this.defensePoint = defensePoint;
    }

}

히어로와 빌런 모두 공통적으로 유닛이며, 유닛의 공통점을 포함 하고 있다.

그림

자바 상속의 특징

1. 다중상속 금지

public class Hellain extends Villain, Hero {
    "컴파일 에러"
}

2. 모든 클래스의 최상위 클래스는 Object 클래스 이다.

자바의 모든 클래스는 최상위 클래스 Object의 서브클래스이다.

public class Main {

    public static void main(String[] args) {
        Object hero = new Hero(100,10,3);// Hero 는 Object의 서브클래스
        Object villain = new Villain(150,5,5);//Villain도 Object의 서브클래스
    }

이 점은 후에 내가 모르는 값을 함수를 통하여 리턴 받아올 때 유용하다. 일단 Object 클래스를 받고 형변환을 통하여 손쉽게 객체를 복수할 수 있다.


super 키워드


super 키워드는 서브클래스가 슈퍼클래스에 접근할 때 사용한다. super는 슈퍼클래스의 참조변수라고 볼 수 있다.

super()를 사용하면 수퍼클래스의 생성자를 호출할 수 있다.

Villain.java


public class Villain extends Unit {

    int defensePoint;

    int hp; // 보통 이렇게 하지 않지만 super를 설명하기 위해 상위클래스에서 사용한 변수명을 다시 사용한다.

    public Villain(int hp, int attackPoint, int defensePoint) {
        super(hp, attackPoint);
        this.defensePoint = defensePoint;
        this.hp = 1000; // 빌런만 가지고있고 유닛은 없는 hp를 1,000으로 설정한다.
        super.hp = 10000; //유닛이 공통으로 가지고있는 hp를 10,000으로 설정한다.
    }

}

메소드 오버라이딩


메소드 오버라이딩은 상위 클래스가 정의한 메소드를 하위 클래스가 가져와 변경하거나 확장하는 기법, 즉 하위 클래스에서 메소드를 재정의하는 기법이다.

week5 참조


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


컴파일타임에는 알 수 없는 메서드의 의존성을 런타임에 늦게 바인딩 하는것이다.

public class Main {

    public static void main(String[] args) {
        Hero hero = new Hero(100,10,3);
        Villain villain = new Villain(150,5,5);
        hero.attack(villain);
        villain.attack(hero);
        System.out.println(hero);
        System.out.println(villain);
    }

}

히어로는 Hero의 attack()을 호출 할 것이고 빌런은 Villain의 attack()을 호출 할 것이다. 이것은 모두 컴파일 타임 에 파악할 수 있다.

Main.class

public static void main(java.lang.String[]);
    Code:
       0: new           #7                  
       3: dup
       4: bipush        100
       6: bipush        10
       8: bipush        30
      10: invokespecial #9                  
      13: astore_1
      14: new           #12                 
      17: dup
      18: sipush        150
      21: iconst_5
      22: iconst_5
      23: invokespecial #14                 
      26: astore_2
      27: aload_1
      28: aload_2
      29: invokevirtual #15         // Method study/moon/test/Hero.attack:(Lstudy/moon/test/Unit;)V
      32: aload_2
      33: aload_1
      34: invokevirtual #19         // Method study/moon/test/Villain.attack:(Lstudy/moon/test/Unit;)V
      37: getstatic     #20                 
      40: aload_1
      41: invokevirtual #26                 
      44: getstatic     #20                 
      47: aload_2
      48: invokevirtual #26                 
      51: return

29행과 34행을 살펴보면 해당 인스턴스는 정확하게 어떤 클래스의 메서드를 호출 할 것인지 명확하게 알 수 있다. hero는 Hero클래스의 attack을 호출, villain은 Villain클래스의 attack을 호출한다.

public class Main {

    public static void main(String[] args) {
        Unit hero = new Hero(100,10,3);// 타입을 Unit으로 변경하였다.
        Unit villain = new Villain(150,5,5);// 타입을 Unit으로 변경하였다.
        hero.attack(villain);
        villain.attack(hero);
        System.out.println(hero);
        System.out.println(villain);
    }

}

타입을 Unit으로 변경하고 실행해보았다.

public static void main(java.lang.String[]);
    Code:
       0: new           #7                  
       3: dup
       4: bipush        100
       6: bipush        10
       8: bipush        30
      10: invokespecial #9                  
      13: astore_1
      14: new           #12                 
      17: dup
      18: sipush        150
      21: iconst_5
      22: iconst_5
      23: invokespecial #14                 
      26: astore_2
      27: aload_1
      28: aload_2
      29: invokevirtual #15         // Method study/moon/test/Unit.attack:(Lstudy/moon/test/Unit;)V
      32: aload_2
      33: aload_1
      34: invokevirtual #15         // Method study/moon/test/Unit.attack:(Lstudy/moon/test/Unit;)V
      37: getstatic     #21                 
      40: aload_1
      41: invokevirtual #27                 
      44: getstatic     #21                 
      47: aload_2
      48: invokevirtual #27                 
      51: return

29행과 34행을 보면 해당 메서드는 Unit의 attack을 사용하기로 결정되어있다. 그렇다면 빌런과 히어로 모두 유닛의 attack을 사용하는것일까?

Hero{hp=98, attackPoint=10, level=3}
Villain{hp=145, attackPoint=5, defensePoint=5} // 유닛의 메서드가아닌 빌런의 메서드가 호출된 결과이다

이처럼 컴파일 타임에는 메서드의 클래스타입이 정해져있지 않지만 런타임에 정해져서 메서드를 호출하는 것을 동적 dispatch 라고 한다.


추상클래스


구체적이지 않은 클래스를 말한다. 예를들어 구체적인 클래스가 히어로, 빌런이라면 추상적인 클래스는 유닛이 될 수 있다. 공통된 부분으로 묶기에는 적당하지만 구현을 하지는 않을 클래스를 만들 때 추상클래스를 이용한다.

public abstract class Unit {
    int hp;
    int attackPoint;

    public Unit(int hp, int attackPoint) {
        this.hp = hp;
        this.attackPoint = attackPoint;
    }

    public abstract void attack(Unit unit);

    public void attackedBy(Unit unit) {
        this.hp -= unit.attackPoint;
    }
}
  • 클래스 앞에 abstract 키워드를 이용하면 해당 클래스는 - 추상클래스가 된다.
  • 추상클래스는 추상메서드를 작성할 수 있다. (추상메서드란, - 구현부가 없는 메서드이다.)
  • 추상메서드는 메서드의 반환형 앞에 absract를 붙이면 된다.
  • 추상클래스는 인스턴스를 생성할 수 없다.
  • 추상 클래스를 상속받은 클래스는 수퍼클래스가 가지고있는 - 추상메서드를구현하지 않으면 추상클래스가 된다.

final 키워드


다시 무언가를 정의내리는것을 막는 키워드이다.

  • final class ~~
    • 클래스의 상속을 막는다.
  • final variable(final String name ~~)
    • 변수의 재할당을 막는다.
  • final String method(Parameter parameter){}
    • 메서드의 오버라이딩을 막는다.

final 키워드를 사용하는 이유

  • 우리의 기억력이 완벽하지 않고, 우리 코드의 의도가 분명하지 - 않기때문에 사용한다.
  • 우리는 항상 실수하기 때문에 final을 사용하여 미리 실수를 - 차단할 수 있는 방안은 모두 사용해야한다.

class Main {

    public static void main(String[] args) {
        final List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        System.out.println(list); // [1, 2, 3, 4, 5]
    }
}

list 내부의 값이 0개에서 5개로 증가했다. 이처럼 final에서의 불변은 대상이 불변하는것이 아니라 새롭게 할당하는것을 막는다는것을 의미한다.


Object 클래스


자바의 최상위 클래스이다. 따로 어디서 상속받지 않더라도 Obejct는 모든 클래스의 최상위 클래스이기 때문에 내가 클래스를 생성하면 그 클래스에는 자동으로 object의 기본메서드가 포함되어있다.

Object 클래스의 기본 메서드

그림




© 2019.04. by salmon2

Powered by theorydb