[이것이자바다] Chapter 07. 상속

kims·2023년 12월 8일
0

이것이자바다

목록 보기
7/9
post-thumbnail

7.1 상속 개념

  • 상속은 부모가 자식에게 물려주는 행위로 중복 코드를 줄여 개발 시간을 단축시키고, 클래스 수정을 최소화할 수 있다.

7.2 클래스 상속

  • 자식이 extends 키워드를 사용해 부모를 선택한다. 단, 다중 상속은 허용하지 않는다.
package ch07.sec02;

public class Phone {

    public String model;
    public String color;

    public Phone() {
        System.out.println("부모 생성자 호출");
    }

    public void bell() {
        System.out.println("[부모] 벨이 울린다.");
    }

    public void hangup() {
        System.out.println("[부모] 전화를 끊습니다.");
    }

    public void sendVoice(String message) {
        System.out.println("[부모] " + message);
    }
}

package ch07.sec02;

public class SmartPhone extends Phone{
    public boolean wifi;

    public SmartPhone(String model, String color) {
        this.model = model;
        this.color = color;
    }

    public void setWifi(boolean wifi) {
        this.wifi = wifi;
        System.out.println("[자식] 와이파이 상태를 변경했습니다.");
    }
}

package ch07.sec02;

public class SmartPhoneEx {
    public static void main(String[] args) {
        SmartPhone smartPhone = new SmartPhone("Galaxy", "Gary");  // 부모 생성자 호출

        System.out.println(smartPhone.model);                      // Galaxy
        System.out.println(smartPhone.color);                      // Gary

        System.out.println(smartPhone.wifi);                       // false

        smartPhone.bell();                                         // [부모] 벨이 울린다.
        smartPhone.sendVoice("Hi~");                               // [부모] Hi~
        smartPhone.hangup();                                       // [부모] 전화를 끊습니다.

        smartPhone.setWifi(true);                                  // [자식] 와이파이 상태를 변경했습니다.
    }
}

7.3 부모 생성자 호출

  • 자식 객체를 생성하면 부모 객체가 먼저 생성된 다음에 자식 객체가 생성된다.
  • 부모 객체에 기본 생성자가 있다면, 자식 생성자의 맨 첫 줄에 컴파일 과정에서 자동으로 super()가 추가된다.
    만약, 부모 클래스에 기본 생성자가 없고 매개변수를 갖는 생성자만 있다면 개발자가 직접 자식 생성자에 super(매개값, …)를 추가해줘야 한다.

1) 부모클래스에 매개변수 갖는 생성자 추가

package ch07.sec02;

public class Phone {

    public String model;
    public String color;
    
 //    public Phone() {
 //        System.out.println("부모 생성자 호출");
 //    }

    public Phone(String model, String color) {
        this.model = model;
        this.color = color;
        System.out.println("[부모] Phone(String model, String color) 생성자 호출");
    }
    
    // 코드 생략…

}
컴파일 에러 발생 : java: constructor Phone in class ch07.sec02.Phone cannot be applied to given types;
해결 방법
    public SmartPhone(String model, String color) {
        super(model, color);
        this.model = model;
        this.color = color;
    }

7.4 메서드 재정의

메서드 오버라이딩

  • 상속된 메서드를 자식 클래스에서 재정의하는 것을 말한다.
  • 부모 메서드의 선언부(리턴 타입, 메서드명, 매개변수)와 동일해야 한다.
  • 접근 제한을 더 강하게 오버라이딩할 수 없다. (public → private로 변경 불가)
    • 컴파일 에러 발생
  • @Override 애노테이션을 붙이면 컴파일 단계에서 정확히 오버라이딩 되었는지 체크하고, 문제가 있다면 커파일 에러를 출력한다.
package ch07.sec02;

public class SmartPhone extends Phone{
    public boolean wifi;

    public SmartPhone(String model, String color) {
        super(model, color);
        this.model = model;
        this.color = color;
    }

    public void setWifi(boolean wifi) {
        this.wifi = wifi;
        System.out.println("[자식] 와이파이 상태를 변경했습니다.");
    }

    @Override
    public void bell() {
        super.bell();
        System.out.println("[자식] 벨이 울린다.");
    }
}


package ch07.sec02;

public class SmartPhoneEx {
    public static void main(String[] args) {
        SmartPhone smartPhone = new SmartPhone("Galaxy", "Gary");   // [부모] Phone(String model, String color) 생성자 호출
        smartPhone.bell();                                          // [자식] 벨이 울린다.
    }
}

부모 메서드 호출

  • super 키워드와 도트.연산자를 사용해 부모 클래스를 호출할 수 있다.
package ch07.sec02;

public class SmartPhone extends Phone{
	// 코드 생략… 

    @Override
    public void bell() {
        super.bell();
        System.out.println("[자식] 벨이 울린다.");
    }
}

package ch07.sec02;

public class SmartPhoneEx {
    public static void main(String[] args) {
        SmartPhone smartPhone = new SmartPhone("Galaxy", "Gary");   // [부모] Phone(String model, String color) 생성자 호출
        smartPhone.bell();                                          // [부모] 벨이 울린다. // [자식]벨이 울린다.
    }
}

7.5 final 클래스와 final 메서드

final 클래스

  • 최종적인 클래스로 더 이상 상속할 수 없다. 즉, 부모 클래스가 될 수 없다.
package ch07.sec05.exam02;

public final class Car {

}

package ch07.sec05.exam02;

public class SportsCar extends Car{   // 컴파일 에러 : Cannot inherit from final 'ch07.sec05.exam02.Car'

}

final 메서드

  • 최종적인 메서드로 오버라이딩할 수 없다.
package ch07.sec05.exam02;

public class Car {

    public int speed;

    public void SpeedUp() {
        speed += 1;
    }

    public final void stop() {
        System.out.println("차가 멈춥니다.");
        speed = 0;
    }
}

package ch07.sec05.exam02;

public class SportsCar extends Car{

    @Override
    public void SpeedUp() {
        speed += 10;
    }

    public final void stop() {  // 컴파일 에러 : 'stop()' cannot override 'stop()' in 'ch07.sec05.exam02.Car'; overridden method is final
        System.out.println("차가 멈춥니다.");
        speed = 0;
    }
}

7.6 protected 접근 제한자

접근 제한자제한 범위
public없음
protected같은 패키지이거나 자식 객체만 사용 가능
default같은 패키지
private객체 내부
  • 같은 패키지에서는 default처럼 접근이 가능하나, 다른 패키지에서는 자식 클래스만 접근을 허용한다.
package ch07.sec06.package1;

public class A {
    protected String field;

    protected A() {}

    protected void method() {
        System.out.println("A 클래스입니다.");
    }
}

package ch07.sec06.package1;

public class B {

    public static void main(String[] args) {
        A a = new A();
        a.method(); // A 클래스입니다.
    }
}

package ch07.sec06.package2;


import ch07.sec06.package1.A;

public class C extends A {

    public C() {
        super();	// 생략 가능
    }

    public void method1() {
        this.method();
    }
}

package ch07.sec06.package2;

public class D {

    public static void main(String[] args) {
        C c = new C();
        c.method1();    // A 클래스입니다.
    }
}

7.7 타입 변환

  • 클래스의 타입 변환은 상속 관계에 있는 클래스 사이에서 발생한다.

자동 타입 변환

  • 자식은 부모와 동일하게 취급될 수 있다.
package ch07.sec07.exam01;

public class Parent {
    public Parent() {}

    public void method01() {
        System.out.println("Parent - method01()");
    }

    public void method02() {
        System.out.println("Parent - method02()");
    }
}

package ch07.sec07.exam01;

public class Child extends Parent {

    public Child() {}

    @Override
    public void method01() {
        System.out.println("@Override : Child - method01()");
    }

    public void method03() {
        System.out.println("Child - method03()");
    }
}

package ch07.sec07.exam01;

public class Ex01 {

    public static void main(String[] args) {
        Parent parent = new Child();   // 자동 타입 변환

        parent.method01();             // @Override : Child - method01()
        // parent.method03();  // 컴파일 에러 : Cannot resolve method 'method03' in 'Parent'

        Child child = new Child();
        Parent parent1 = child;

        System.out.println(parent1 == child);   // true

    }
}

  • 부모타입으로 자동 타입 변환된 후에는 부모 클래스에 선언된 필드와 메서드만 접근이 가능하다.
    단, 자식 클래스에서 오버라이딩된 메서드가 있다면 부모 메서드 대신 오버라이딩된 메서드가 호출된다.

강제 타입 변환

  • 부모 타입은 자식 타입으로 자동 변환되지 않기 때문에 강제 타입 변환을 해야 한다.
    단, 자식 → 부모 타입 변환 후 다시 부모 → 자식으로 변환할 때만 사용할 수 있다.
package ch07.sec07.exam01;

public class Ex01 {

    public static void main(String[] args) {
        // Child child = (Child) new Parent(); // 런타임 에러 : ClassCastException Exception

        Parent parent = new Child();
        Child child = (Child) parent;
        child.method03();   // Child - method03()
    }
}

7.8 다형성

  • 사용 방법은 동일하지만 실행 결과가 다양하게 나오는 성질

필드 다형성

  • 필드 타입은 동일하지만 대입되는 객체가 달라져 실행 결과가 다양하게 나올 수 있는 것
package ch07.sec08.exam;

public class Chef {

    public Menu menu;

    public void cook() {
        menu.make();
    }

    public void order(Menu menu) {
        menu.prepare();
    }
}


package ch07.sec08.exam;

public class Menu {

    public void make() {
        System.out.println("요리하다");
    }

    public void prepare() {
        System.out.println("메뉴를 준비하다");
    }
}


package ch07.sec08.exam;

public class KoreanFood extends Menu {

    @Override
    public void make() {
        System.out.println("한식을 요리하다");
    }

    @Override
    public void prepare() {
        System.out.println("한식을 준비하다");
    }
}

package ch07.sec08.exam;

package ch07.sec08.exam;

public class WesternFood extends Menu {

    @Override
    public void make() {
        System.out.println("양식을 요리하다");
    }

    @Override
    public void prepare() {
        System.out.println("양식을 준비하다");
    }
}

package ch07.sec08.exam;

public class Ex {

    public static void main(String[] args) {
        Chef chef = new Chef();

        chef.menu = new Menu();
        chef.cook();                    // 요리하다

        chef.menu = new KoreanFood();
        chef.cook();                    // 한식을 요리하다

        chef.menu = new WesternFood();
        chef.cook();                    // 양식을 요리하다
    }
}


어떤 객체가(e.g. Menu, KoreanFood, WesternFood) 대입됐는지에 따라 cook()의 실행 결과가 달라지게 된다.

매개변수 다형성

  • 메서드가 클래스 타입의 매개변수를 가지고 있으면, 같은 타입의 객체뿐만 아니라 자식 객체를 제공할 수 있다.
package ch07.sec08.exam;

public class Ex {

    public static void main(String[] args) {
        Chef chef = new Chef();

        Menu menu = new Menu();
        chef.order(menu);          // 메뉴를 준비하다

        KoreanFood koreanFood = new KoreanFood();
        chef.order(koreanFood);    // 한식을 준비하다

        WesternFood westernFood = new WesternFood();
        chef.order(westernFood);   // 양식을 준비하다
    }
}

어떤 객체가(e.g. Menu, KoreanFood, WesternFood) 대입됐는지에 따라 order()의 실행 결과가 달라지게 된다.

7.9 객체 타입 확인

  • instanceof 연산자 사용
  • 어떤 객체가 매개값으로 제공되었는지 확인하거나 변수가 참조하는 객체의 타입을 확인하고자 할 때 사용
package ch07.sec08.exam;

public class Ex {

    public static void menuInfo(Menu menu) {
        if (menu instanceof KoreanFood food) {
            KoreanFood koreanFood = (KoreanFood) menu;
            koreanFood.prepare();
//            food.prepare(); // java 12부터
        } else if (menu instanceof WesternFood food) {
            WesternFood westernFood = (WesternFood) menu;
            westernFood.prepare();
//            food.prepare(); // java 12부터
        }
    }

    public static void main(String[] args) {
        Menu koreanMenu = new KoreanFood();
        Menu westernMenu = new WesternFood();

        System.out.println(koreanMenu instanceof KoreanFood);       // true
        System.out.println(westernMenu instanceof WesternFood);     // true

        System.out.println(koreanMenu instanceof  WesternFood);     // false
        System.out.println(westernMenu instanceof  KoreanFood);     // false

        menuInfo(koreanMenu);    // 한식을 준비하다
        menuInfo(westernMenu);   // 양식을 준비하다
    }
}
  • 객체가 타입이면 true, 그렇지 않으면 false
  • 자바 12부터는 instanceof 연산의 결과가 true일 경우, 우측 타입 변수를 바로 사용할 수 있다.
    따라서 강제 타입 변환이 필요 없다.

7.10 추상 클래스

  • 객체를 생성할 수 있는 클래스들의 공통적인 필드나 메서드를 추출해서 선언한 클래스로 new 연산자를 사용해 객체를 직접 생성할 수 없다.
  • 추상 클래스는 객체를 생성할 수 있는 클래스의 부모 역할만을 한다.

추상 클래스 선언

  • 클래스 선언 시 abstract 키워드를 붙이면 추상 클래스가 된다.
package ch07.sec10.exam01;

public abstract class Phone {
    // 필드 선언
    String owner;

    // 생성자 선언
    public Phone(String owner) {
        this.owner = owner;
    }

    //메서드 선언
    void turnOn() {
        System.out.println("전원 ON");
    }

    void turnOff() {
        System.out.println("전원 OPF");
    }

    public abstract  void sound();
}

package ch07.sec10.exam01;

public class SmartPhone extends Phone{
    public SmartPhone(String owner) {
        super(owner);
    }

    void internetSearch() {
        System.out.println("인터넷 검색을 하다.");
    }

    @Override
    public void sound() {
        System.out.println("삐리리리~");
    }
}


package ch07.sec10.exam01;

public class PhoneEx {

    public static void main(String[] args) {

        // Phone phone = new Phone();  // 컴파일 에러 : 'Phone' is abstract; cannot be instantiated

        SmartPhone smartPhone = new SmartPhone("홍길동");
        smartPhone.turnOn();            // 전원 ON
        smartPhone.turnOff();           // 전원 OPF
        smartPhone.internetSearch();    // 인터넷 검색을 하다.
        smartPhone.sound();             // 삐리리리~
    }
}

추상 메서드와 재정의

  • 추상 메서드란 abstract 키워드가 붙고, 메서드 실행 내용인 중괄호가 {}가 없다.
  • 추상 메서드는 자식 클래스의 공통 메서드라는 것만 정의할 뿐, 실행 내용을 가지지 않는다.
  • 추상 메서드는 자식 클래스에서 반드시 재정의(오버라이딩)해서 실행 내용을 채워야 한다.

7.11 봉인된 클래스

  • Java 15부터 도입된 봉인된(sealed) 클래스는 무분별한 자식 클래스 생성을 방지한다.
  • sealed 키워드를 사용하면 permits 키워드 뒤에 상속 가능한 자식 클래스를 지정해야 한다.
  • 자식 클래스는 final, non-sealed, selaed 중 하나의 유형으로 선언되어야 한다.
package ch07.sec10.exam02;

public sealed class Person permits Employee, Manager, Owner{

    public void work() {
        System.out.println("하는 일이 결정되지 않았습니다.");
    }
}

  • final 키워드는 더 이상 상속할 수 없다.
package ch07.sec10.exam02;

public final class Employee extends Person {

    @Override
    public void work() {
        System.out.println("제품을 생산합니다.");
    }
}

  • non-sealed 키워드는 봉인을 해제한다는 뜻으로, 자식 클래스를 만들 수 있다.
package ch07.sec10.exam02;

public non-sealed class Manager extends Person {
    @Override
    public void work() {
        System.out.println("생산을 관리합니다.");
    }
}

package ch07.sec10.exam02;

public class SalesManager extends Manager{
    @Override
    public void work() {
        System.out.println("영업을 관리합니다.");
    }
}

package ch07.sec10.exam02;

public sealed class Owner extends Person permits Company { // sealed, non-sealed or final modifiers expected

    @Override
    public void work() {
        System.out.println("회사를 운영합니다.");
    }
}

package ch07.sec10.exam02;

public final class Company extends Owner {
    String name;

    public Company(String name) {
        this.name = name;
    }

    public void operate() {
        System.out.println(this.name + " 회사가 운영 중입니다.");
    }
}

package ch07.sec10.exam02;

public class SealedEx {

    public static void main(String[] args) {
        Person person = new Person();
        person.work();          // 하는 일이 결정되지 않았습니다.

        Employee employee = new Employee();
        employee.work();        // 제품을 생산합니다.

        Manager manager = new Manager();
        manager.work();         // 생산을 관리합니다.

        SalesManager salesManager = new SalesManager();
        salesManager.work();    // 영업을 관리합니다.

        Owner owner = new Owner();
        owner.work();           // 회사를 운영합니다.

        Company company = new Company("화장품");
        company.operate();      // 화장품 회사가 운영 중입니다.

    }
}

profile
기술로 세상을 이롭게

0개의 댓글