클래스의 상속3: 상속의 목적

gustjtmd·2022년 1월 29일
0

Java

목록 보기
11/40

상속이 도움이 되는 상황의 소개

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

단순한 인맥 관리 프로그램

상속이 없이 인맥 관리 프로그램을 작성해보자
이 프로그램을 통해서 주변 사람들의 정보를 관리하는 것이 목적이다
저장 및 관리할 대상은 다음과 같이 두 부류로 나누었다.

1. 대학 동창	이름,전공,전화번호 정보 저장 및 관리
2. 직장 동료	이름,부서,전화번호 정보 저장 및 관리

코드로 확인해보자

class UnivFriend{   //대학 동창
    private String name;
    private String major;   //전공
    private String phone;

    public UnivFriend(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{   //직장 동료
    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[5];
        int unct = 0;

        //직장 동료의 관리를 위한 배열과 변수
        CompFriend[] cfrns = new CompFriend[5];
        int ccnt = 0;

        //대학 동창의 정보 저장
        ufrns[unct++] = new UnivFriend("Lee","Computer","010-111-111");
        ufrns[unct++] = new UnivFriend("Seo","Electronics","010-222-222");

        //직장 동료의 정보 저장
        cfrns[ccnt++] = new CompFriend("YOON","R&D1","02-111-111");
        cfrns[ccnt++] = new CompFriend("PARK","R&D2","02-222-222");

        //모든 동창 및 동료의 정보 전체 출력
        for(int i = 0; i<unct;i++) {
            ufrns[i].showInfo();
            System.out.println();
        }
        System.out.println("====================================================\n");
        for(int i = 0; i<ccnt;i++){
            cfrns[i].showInfo();
            System.out.println();
        }
    }
}


이름 : Lee
전공 : Computer
전화 : 010-111-111

이름 : Seo
전공 : Electronics
전화 : 010-222-222

===========================================================================

이름 : YOON
부서 : R&D1
전화 : 02-111-111

이름 : PARK
부서 : R&D2
전화 : 02-222-222

-----------------------------------------------------------------------

위 코드의 main 메소드를 관찰하면

1. 인스턴스를 저장하는 배열이 두개이다.

2. 대학 동창의 정보를 저장하는 과정과 직장 동료의 정보를 저장하는 과정이 나뉜다
	-> 저장에 필요한 배열과 변수가 다르기 때문이다.
    
3. 저장된 정보를 모두 출력할때 두개의 for문을 작성해야 한다
	-> 출력에 사용되는 배열과 변수가 다르기 때문이다
    

이러한 형태의 구현은 문제가 될 수 밖에 없다.
"위의 코드와 같이 프로그램을 작성한다면 배열의 수가 얼마나 더 늘어날지 모른다."
"그리고 늘어나는 배열의 수만큼 프로그램은 더 복잡해진다."

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

상속의 장점으로 문제점을 해결 할 수 있다
"연관된 일련의 클래스들에 대해 공통적인 규약을 정의하여서"

코드로 확인해보자

class Friend{
    private String name;
    private 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 UnivFriend extends Friend{
    private String major;  //전공

    public UnivFriend(String name, String phone, String major) {
        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 phone, String department) {
        super(name, phone);
        this.department = department;
    }

    public void showInfo(){
        super.showInfo();
        System.out.println("부서 : "+department);
    }
}
public class MyFriends2 {
    public static void main(String[] args) {
        Friend[] frns = new Friend[10];
        int fcns = 0;

        frns[fcns++] = new UnivFriend("Lee","010-111-111","Computer");
        frns[fcns++] = new UnivFriend("SEO","010-222-222","Electronics");
        frns[fcns++] = new CompFriend("Yoon","02-111-111","R&D1");
        frns[fcns++] = new CompFriend("PARK","02-222-222","R&D2");

        for (int i = 0 ; i<fcns; i++) {
            frns[i].showInfo();
            System.out.println();
        }
    }
}


이름 : Lee
전화번호 : 010-111-111
전공 : Computer

이름 : SEO
전화번호 : 010-222-222
전공 : Electronics

이름 : Yoon
전화번호 : 02-111-111
부서 : R&D1

이름 : PARK
전화번호 : 02-222-222
부서 : R&D2


-------------------------------------------------------------------------
위 코드에서 UnivFreind 클래스와 CompFriend 클래스가 Friend 클래스를 상속하게 함으로써
다음과 같은 효과를 얻었다

1) 인스턴스를 저장하는 배열이 하나이다
	-> Friend 클래스를 상속하는 클래스가 더 추가되어도 이 사실은 변함이 없다
    
2) 정보를 저장하는 과정이 나뉘지 않는다
	-> 하나의 배열에 모든 인스턴스를 저장할 수 있다.
    
3) 저장된 정보를 모두 출력할 때 하나의 for문으로 충분하다
	-> 하나의 배열이 사용되었고 또 메소드 오버라이딩이 도움이 되었다.
    
절대 Friend 클래스를 재활용된 클래스로 보면 안된다
"UnivFriend 클래스와 CompFriend 클래스에 공통 규약을 적용하기 위해 정의된 클래스"

Object 클래스와 final 선언 @Override

모든 인스턴스는 System.out.println의 인자로 전달될 수 있다 그리고 인스턴스가 인자로 전달되면 toString 메소드가 호출되면서 이때 반환되는 문자열이 출력되는 것이다.

모든 클래스는 Object 클래스를 상속


클래스를 정의할때 어떤 클래스도 상속하지 않으면 해당 클래스는 java.lang 패키지에 묶여있는
Object 클래스를 상속하게 된다

class MyClass{...} == class MyClass extends Object{...}

자바의 모든 클래스가 Object 클래스를 직접 혹은 간접적으로 상속하도록 한 이유는 무엇일까
이는 자바의 모든 인스턴스에 공통된 기준 및 규약을 적용하기 위함이다

자바의 모든 인스턴스는 다음 메소드의 인자로 전달될 수 있다

public void println(Ojbect x)
	->System.out.pirnltn 메소드
    
위 메소드의 매개변수 형이 Object이다 따라서 모든 인스턴스는 위 메소드의 인자가 될수있다.
그리고 위의 메소드는 인자로 전달된 인스턴스의 다음 메소드를 호출한다
이 메소드는 Object 클래스에 정의되어있는 메소드이므로 모든 인스턴스를 대상으로 호출 가능.

public String toSTring()

코드로 확인해보자

class Cake{
    //Object 클래스의 toString 메소드를 오버라이딩
    public String toString(){
        //Object 클래스의 toString 메소드 호출하여 반환 결과 출력
        System.out.println(super.toString());
        return "My birthday cake";
    }
}
class CheeseCake extends Cake{
    //Cake 클래스의 toString 메소드를 오버라이딩
    public String toString(){
        return "My birthday cheese cake";
    }
}
public class OverridingToString {
    public static void main(String[] args) {
        Cake c1 = new Cake();
        Cake c2 = new CheeseCake();

        //c1이 참조하는 인스턴스의 toString 메소드 호출로 이어짐
        System.out.println(c1);
        System.out.println();

        //c2가 참조하는 인스턴스의 toString 메소드 호출로 이어짐
        System.out.println(c2);
    }
}


Cake@3cb5cdba
My birthday cake

My birthday cheese cake

--------------------------------------------------------------------

위 코드에서 Object 클래스의 toString 메소드를 호출하여 이때 반환되는 문자열을 출력하였다.

System.out.println(super.toString());

출력 결과 클래스의 이름과 @의 뒤를 이어 이해할 수 없는 숫자가 출력되는데
자바 문서의 toString을 설명한 부분에서 이것이 어떻게 구성된 문자열인지 설명하고 있지만
중요하진 않다

오히려 클래스를 정의할때 toString 메소드를 가급적 오버라이딩 하라고 조언하고 있는데
간결하고 읽기 쉬우면서 인스턴스 구분에 도움이 되는 문자열을 구성하면서 반환하도록
오버라이딩 하라고 조언하고 있다

클래스와 메소드의 final 선언

클래스를 정의하는데 있어서 해당 클래스가 다른 클래스가 상속하는 것을 원치 않다면
final 선언을 추가하자

public final class 클래스이름{...}
-> 이 클래스는 다른 클래스가 상속할 수 없음

대표적인 final클래스로 String 클래스가 있다. 따라서 우리는 String 클래스를 상속할수 없다.

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

class Simple{
	//아래의 메소드는 다른 클래스에서 오버라이딩 불가능
    public final void func(int n){...}
}

@Override

오버라이딩 위에 @Override를 선언하면
이는 다음의 메시지를 컴파일에게 전달하는 셈이 된다
"이 메소드는 상위 클래스의 메소드를 오버라이딩 할 목적으로 정의 하였습니다."

@Override
public double add(double a, double b){...}

만약 오버라이딩이 제대로 안되면 컴파일러가 컴파일 오류로 알려준다.
profile
반갑습니다

0개의 댓글