새소식

Back/Java

상속과 추상 그리고 final

  • -

목차


  • 상속
  • super
  • 메서드 오버라이딩
  • Dynamic Method Dispatch
  • final
  • 추상 클래스




상속


  • 부모님이 자식들에게 재산을 물려 주는 것과 같다.
  • 부모 클래스의 자원을 자식 클래스가 사용하게 된다.
  • 자식 클래스는 다중 상속이 불가능 하다.
    • 부모 입장에서 자식들에게 재산을 각각 물려줄 수 있다.
    • 자식 입장에서 부모는 하나 이다.
  • 상속은 extends 키워드를 사용 한다.
    • class 자식 extends 부모 { }
// 부모 클래스
class foo {  }


// 자식 클래스
class Bar extends foo {  }


// 자식 클래스
class Baz extends foo {  }
// =============================
// 다중 상속 안됨
// class Baz extends Foo, Bar { }




super


  • 부모님의 재산을 물려받은 자식이 부모의 재산을 사용하는 것과 같다.
  • super()
    • 자식 생성자의 맨 처음 줄에 작성해야 한다.
    • 부모님의 생성자를 호출한다.
  • super
    • 자식의 생성자에서 작성해야 한다.
    • 부모님의 필드를 호출한다.
// 부모 클래스
class Foo {

    public int parentAvgTall;   // 부모님의 평균 키
    public int parentAvgWeight; // 부모님의 평균 몸무게

    public Foo() {
        this.parentAvgTall = 170;
        this.parentAvgWeight = 60;

        System.out.println("   부모님의 평균 키 : " + this.parentAvgTall);
        System.out.println("부모님의 평균 몸무게 : " + this.parentAvgWeight);
    }
}


// 자식 클래스
class Bar extends Foo {

    public int barSelfTall;   // 나의 키
    public int barSelfWeight; // 나의 몸무게

    public Bar() {
        super(); // 부모님의 생성자를 호출

        this.barSelfTall = 175;
        this.barSelfWeight = 70;

        System.out.println("   Bar의 키 : " + this.barSelfTall);
        System.out.println("Bar의 몸무게 : " + this.barSelfWeight);
    }
}


// 자식 클래스
class Baz extends Foo {

    public int bazSelfTall;   // 나의 키
    public int bazSelfWeight; // 나의 몸무게

    public Baz() {
        this.bazSelfTall = 160;
        this.bazSelfWeight = 50;

        System.out.println("   부모님의 평균 키 : " + super.parentAvgTall);
        System.out.println("부모님의 평균 몸무게 : " + super.parentAvgWeight);

        System.out.println("   Baz의 키 : " + this.bazSelfTall);
        System.out.println("Baz의 몸무게 : " + this.bazSelfWeight);
    }
}




메소드 오버라이딩


  • 부모님의 메서드를 자식들이 재정의 하여 사용하는 것 이다.
  • 예를들어 부모님의 자동차를 자식이 물려 받아 새로 꾸며 사용하는 것 이다.
    • 하지만 자동차의 이름은 그대로 이다.
  • 오버라이딩은 재정의 하여 사용 하는 것 이다.
  • 부모님의 메서드와 오버라이딩한 자식의 메서드의 메서드 선언부는 같아야 한다.
    • 리턴형식 메서드명 (매개변수) { } 모두 같아야 한다.
  • 보무님의 접근제한자보다 낮은 범위로 선언할 수 없다.
// 부모 클래스
class Foo {

  public int parentAvgTall;   // 부모님의 평균 키
  public int parentAvgWeight; // 부모님의 평균 몸무게

  public Foo() {
    this.parentAvgTall = 170;
    this.parentAvgWeight = 60;

    System.out.println("    부모님의 평균 키 : " + this.parentAvgTall);
    System.out.println("부모님의 평균 몸무게 : " + this.parentAvgWeight);
  }

  public void sonata() {
    System.out.println("Foo의 소나타 자동차 이다.");
  }
}


// 자식 클래스
class Bar extends Foo {

  public int barSelfTall;   // 나의 키
  public int barSelfWeight; // 나의 몸무게

  public Bar() {
    super(); // 부모님의 생성자를 호출

    this.barSelfTall = 175;
    this.barSelfWeight = 70;

    System.out.println("    Bar의 키 : " + this.barSelfTall);
    System.out.println("Bar의 몸무게 : " + this.barSelfWeight);
  }

  @Override
  public void sonata() {
    System.out.println("부모님께 물려 받은 Bar의 소나타 이다.");
  }
}


// 자식 클래스
class Baz extends Foo {

  public int bazSelfTall;   // 나의 키
  public int bazSelfWeight; // 나의 몸무게

  public Baz() {
    this.bazSelfTall = 160;
    this.bazSelfWeight = 50;

    System.out.println("    Baz의 키 : " + this.bazSelfTall);
    System.out.println("Baz의 몸무게 : " + this.bazSelfWeight);

    System.out.println("    부모님의 평균 키 : " + super.parentAvgTall);
    System.out.println("부모님의 평균 몸무게 : " + super.parentAvgWeight);
  }

  @Override
  public void sonata() {
    System.out.println("부모님께 물려 받은 Baz의 소나타 이다.");
  }
}




Dynamic Method Dispatch


  • 런타임시 어떤 클래스를 사용할지 지정하는 방법이다.
  • 런타임 시점에 역할을 분리할 수 있다.
    • 역할을 분리하면 각 상황에 맞게 로직을 분리할 수 있다.
    • 상황에 알맞게 로직을 바꾸어 선언해 줄 수 있다.
  • 상속 관계여야 한다.
  • 부모님의 자원을 상속 받은 자식 클래스는 부모님을 따라갈 수 있다.
  • 반대되는 말은 Static Method Dispatch 이다.
    • 런타임시 자기 자신을 그대로 지정하는 방법이다.
package subclasses;


// 부모 클래스
class Foo {

    public void sonata() {
        System.out.println("Foo의 소나타 자동차 이다.");
    }
}


// 자식 클래스
class Bar extends Foo {

    @Override
    public void sonata() {
        System.out.println("부모님께 물려 받은 Bar의 소나타 이다.");
    }
}


// 자식 클래스
class Baz extends Foo {

    @Override
    public void sonata() {
        System.out.println("부모님께 물려 받은 Baz의 소나타 이다.");
    }
}


public class SubClassBasic {

    public static void main(String[] args) {

        // Dynamic Method Dispatch
        Foo fooBar = new Bar();
        Foo fooBaz = new Baz();

        fooBar.sonata();
        fooBaz.sonata();
    }
}
// =============================================================================
// 부모님께 물려 받은 Bar의 소나타 이다.
// 부모님께 물려 받은 Baz의 소나타 이다.




final


  • 고유함을 나타낸다.
  • 상속 및 오버라이딩이 불가능하다.
  • 방식
    • final 클래스
    • final 필드 변수
    • final 메서드
// final class
final class finalClass {

}

// final 클래스는 상속 불가
//class basicClass extends finalClass { }

class finalBasicClass {

    // final 필드 변수
    final String str = "Hello Final!";

    // final 메서드
    final void print() {
        System.out.println("final 메서드는 오버라이딩 불가능");
    }

    public String getStr() {
        return str;
    }

    public void setStr(String str) {
        // final 필드 변수는 값을 수정할 수 없음
        this.str = str;
    }
}

class finalOtherClass extends finalBasicClass {

    @Override
    void print() {
        // 오버라이딩 불가능
    }
}




추상 클래스


  • 추상 클래스는 꼭 필요한 기능을 모아 놓은 곳 이다.
  • 보통 상속시 부모 클래스에 추상 클래스를 상속 시킨다.
  • 상속받은 부모 클래스는 상속 클래스의 내용을 무조건 재정의 해야 한다.
  • class 앞에 abstract를 붙인다.
package subclasses;

// =============================================================================

abstract class abstractClass {

    // 필수로 선언해야 할 메서드
    abstract void sonata();
}


// 부모 클래스
class Foo extends abstractClass {

    @Override
    public void sonata() {
        System.out.println("Foo의 소나타 자동차 이다.");
    }
}


// 자식 클래스
class Bar extends Foo {

    @Override
    public void sonata() {
        System.out.println("부모님께 물려 받은 Bar의 소나타 이다.");
    }
}


// 자식 클래스
class Baz extends Foo {

    @Override
    public void sonata() {
        System.out.println("부모님께 물려 받은 Baz의 소나타 이다.");
    }
}


// =============================================================================


public class SubClassBasic {

    public static void main(String[] args) {

        // Dynamic Method Dispatch
        Foo fooBar = new Bar();
        Foo fooBaz = new Baz();

        fooBar.sonata();
        fooBaz.sonata();
    }
}




최종 코드


package subclasses;

// 부모 클래스
//class Foo {  }


// 자식 클래스
//class Bar extends Foo {  }


// 자식 클래스
//class Baz extends Foo {  }


// 다중 상속 안됨
// class Baz extends Foo, Bar { }

// =============================================================================

// final class
final class finalClass {

}


// final 클래스는 상속 불가
//class basicClass extends finalClass { }


class finalBasicClass {

    // final 필드 변수
    final String str = "Hello Final!";

    // final 메서드
    final void print() {
        System.out.println("final 메서드는 오버라이딩 불가능");
    }

    public String getStr() {
        return str;
    }

    public void setStr(String str) {
        // final 필드 변수는 값을 수정할 수 없음
        this.str = str;
    }
}


class finalOtherClass extends finalBasicClass {

//    @Override
//    void print() {
        // 오버라이딩 불가능
//    }
}


abstract class abstractClass {

    // 필수로 선언해야 할 메서드
    abstract void sonata();
}


// 부모 클래스
class Foo extends abstractClass {

    public int parentAvgTall;   // 부모님의 평균 키
    public int parentAvgWeight; // 부모님의 평균 몸무게

    public Foo() {
        this.parentAvgTall = 170;
        this.parentAvgWeight = 60;

        System.out.println("    부모님의 평균 키 : " + this.parentAvgTall);
        System.out.println("부모님의 평균 몸무게 : " + this.parentAvgWeight);
    }

    @Override
    public void sonata() {
        System.out.println("Foo의 소나타 자동차 이다.");
    }
}


// 자식 클래스
class Bar extends Foo {

    public int barSelfTall;   // 나의 키
    public int barSelfWeight; // 나의 몸무게

    public Bar() {
        super(); // 부모님의 생성자를 호출

        this.barSelfTall = 175;
        this.barSelfWeight = 70;

        System.out.println("    Bar의 키 : " + this.barSelfTall);
        System.out.println("Bar의 몸무게 : " + this.barSelfWeight);
    }

    @Override
    public void sonata() {
        System.out.println("부모님께 물려 받은 Bar의 소나타 이다.");
    }
}


// 자식 클래스
class Baz extends Foo {

    public int bazSelfTall;   // 나의 키
    public int bazSelfWeight; // 나의 몸무게

    public Baz() {
        this.bazSelfTall = 160;
        this.bazSelfWeight = 50;

        System.out.println("    Baz의 키 : " + this.bazSelfTall);
        System.out.println("Baz의 몸무게 : " + this.bazSelfWeight);

        System.out.println("    부모님의 평균 키 : " + super.parentAvgTall);
        System.out.println("부모님의 평균 몸무게 : " + super.parentAvgWeight);
    }

    @Override
    public void sonata() {
        System.out.println("부모님께 물려 받은 Baz의 소나타 이다.");
    }
}


// =============================================================================


public class SubClassBasic {

    public static void main(String[] args) {

//        Bar bar = new Bar();
//        Baz baz = new Baz();

        // Dynamic Method Dispatch
        Foo fooBar = new Bar();
        Foo fooBaz = new Baz();

        fooBar.sonata();
        fooBaz.sonata();
    }
}

'Back > Java' 카테고리의 다른 글

인터페이스  (0) 2022.07.09
Package, Import, Classpath, 접근제한자  (0) 2022.07.02
JUnit  (0) 2022.06.18
클래스  (0) 2022.06.18
문법 (제어문, 조건문, 반복문)  (0) 2022.06.12
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.