[윤성우의 열혈 Java 프로그래밍] Chapter 16 - 클래스의 상속 3: 상속의 목적

Update:     Updated:

카테고리:

태그:

16-1. 상속이 도움이 되는 상황의 소개

상속을 하는 이유: 연관된 일련의 클래스들에 대해 공통적인 규약을 정의할 수 있다.

- 단순한 인맥 관리 프로그램

다음과 같은 두 부류의 인맥을 관리하는 프로그램이 있다고 가정하자.
1) 대학 동창: 이름, 전공, 전화번호
2) 직장 동료: 이름, 부서, 전화번호

class UniFriend extends Friend{ // 대학 동창
    private String name;
    private String major;   // 전공
    private String phone;

    public UniFriend(String name, String major, String phone) {
        this.name = name;
        this.major = major;
        this.phone = phone;
    }

    public void showInfo() {
        System.out.println("이름: " + name);
        System.out.println("전공: " + major);
        System.out.println("전화: " + phone);
    }
}

class CompFriend extends Friend{    // 직장 동료
    private String name;
    private String department;  // 부서
    private String phone;

    public CompFriend(String name, String department, String phone) {
        this.name = name;
        this.department = department;
        this.phone = phone;
    }

    public void showInfo() {
        System.out.println("이름: " + name);
        System.out.println("부서: " + department);
        System.out.println("전화: " + phone);
    }
}

public class MyFriends {
    public static void main(String[] args) {
        UnivFriend[] ufrns = new UnivFriend[2]; // 대학 친구
        CompFriend[] cfrns = new CompFriend[2]; // 직장 친구

        ufrns[0] = new UniFriend("LEE", "Computer", "010-999-9999");
        ufrns[1] = new UniFriend("KIM", "Social", "010-888-8888");
        
        cfrns[0] = new CompFriend("LEE", "Dev", "010-777-7777");
        cfrns[1] = new CompFriend("KIM", "Market", "010-666-6666");

        for (UnivFriend u: ufrns) {
            ufrns.showInfo();
        }

        for (UnivFriend u: ufrns) {
            cfrns.showInfo();
        }
    }
}

코드를 잘 관찰해보면 다음을 알 수 있다.

  1. 인스턴스를 저장하는 배열이 두 개이다.
  2. 친구의 부류에 따라 정보를 저장하는 과정이 나뉜다. -> 저장에 필요한 배열과 변수가 다르기 때문
  3. 저장된 정보를 출력할 때 두 개의 for문을 작성해야 한다. -> 출력에 사용되는 배열과 변수가 다르기 때문

이렇게 배열이 두 개이므로 무엇을 하건 그 과정이 둘로 나뉜다.
대상마다 배열이 필요하게 되면 늘어나는 배열의 수만큼 프로그램은 더 복잡해진다.

- 인맥 관리 프로그램의 문제를 상속으로 해결하자.

그렇다면, UnivFriend 클래스와 CompFriend 클래스에 공통적인 규약을 적용하여 코드를 개선해보자!

class Friend {
    protected String name;
    protected String phone;

    public Friend(String name, String phone) {
        this.name = name;
        this.phone = phone;
    }
    public void showInfo() {
        System.out.println("이름: " + name);
        System.out.println("전화: " + phone);
    }
}

class UniFriend extends Friend{
    private String major;

    public UniFriend(String name, String major, String phone) {
        super(name, phone);
        this.major = major;

    }
    public void showInfo() {
        super.showInfo();
        System.out.println("전공: " + major);
    }
}

class CompFriend extends Friend{
    private String department;

    public CompFriend(String name, String department, String phone) {
        super(name, phone);
        this.department = department;
    }
    public void showInfo() {
        super.showInfo();
        System.out.println("부서: " + department);
    }
}

public class MyFriends {
    public static void main(String[] args) {
        Friend[] friends = new Friend[4];

        friends[0] = new UniFriend("LEE", "Computer", "010-999-9999");
        friends[1] = new UniFriend("KIM", "Social", "010-888-8888");
        friends[2] = new CompFriend("LEE", "Dev", "010-777-7777");
        friends[3] = new CompFriend("KIM", "Market", "010-666-6666");

        for (Friend f: friends) {
            f.showInfo();
            System.out.println();
        }
    }
}

Friend 클래스를 만듦으로써 다음과 같은 효과를 얻었다.

  1. 인스턴스를 저장하는 배열이 하나이다.
  2. 정보를 저장하는 과정이 나뉘지 않는다.
  3. 저장된 정보를 모두 출력할 때 하나의 for문으로 충분하다.

Friend 클래스는 코드의 재활용 같은 목적으로 추가한 클래스가 아니다.
UnivFriend 클래스와 CompFriend 클래스에 공통 규약을 적용하기 위해 정의된 클래스이다.

16-2. Object 클래스와 final 선언 그리고 @Override

- 모든 클래스는 Object 클래스를 상속한다.

자바의 모든 클래스는 직접 혹은 간접적으로 java.lang 패키지에 묶여 있는 Object 클래스를 상속하게 된다.
이는 자바의 모든 인스턴스에 공통된 기준 및 규약을 적용하기 위함이다.

- 클래스와 메소드의 final 선언

클래스를 정의할 때 해당 클래스를 다른 클래스가 상속하는 것을 원치 않는다면, 앞에 final 선언을 붙여주면 된다.

public final class MyLastCLS {...}

메소드의 정의에 final 선언을 추가하여 해당 메소드의 오버라이딩을 허용하지 않을 수도 있다.

class Simple {
    public final void func(int n){...}
}

- @Override

개발자의 실수로 인한 오류는 컴파일단계에서 발견되는 것이 가장 좋다.
하지만 다음의 예시처럼 컴파일 시 오류가 발생하지 않는 상황도 존재한다.

class ParentAdder {
    public int add(int a, int b) {
        return a + b;
    }
}

class ChildAdder extends ParentAdder {
    // 상위 클래스의 add를 오버라이딩 하려는 의도 -> 오버로딩이 되어버렸다..
    public double add(double a, double b) {
        return a + b;
    }
}

public class OverrideMistake {
    public static void main(String[] args) {
        ParentAdder adder = new ChildAdder();
        System.out.println(adder.add(3, 4));
    }
}

어노테이션은 ‘자바 컴파일러에게 메시지를 전달하는 목적의 메모’이다.
다음과 같이 어노테이션을 달아주면 된다.

class ChildAdder extends ParentAdder {
    @Override
    public double add(double a, double b) {
        return a + b;
    }
}

이렇게 달아놓으면 컴파일러는 오버라이딩이 제대로 되었는지 확인을 하고, 개발자의 의도대로 되지 않았다면 컴파일 단계에서 검증해준다.

Java lang 카테고리 내 다른 글 보러가기

댓글 남기기