제이제이
article thumbnail

 

본 내용은 "윤성우의 열혈 Java 프로그래밍"를 참고로 정리한 내용입니다.

 

1. 상속을 적용하여 도움이 되는 상황


이번 포스팅에서는 간단하게 상속을 적용하여 보다 좋은 코드를 만드는 상황에 대해서 살펴봅시다.
❓ 상속이란?

 🔥 연관되어 있는 클래스에 대해서 공통적인 규약을 정해 놓는 것을 의미합니다.

 

스마트폰에 저장된 전화번호를 출력하는 시스템

프로그램을 통해 스마트폰에 있는 전화번호 중 다음과 같이 주변 지인들을 구별해야 하는 상황을 가정하고 코드를 살펴보겠습니다.

  1. 대학 동창 친구들 = 이름, 전공, 개인 전화번호를 구별하여 저장함
  2. 직장 동료분들 = 이름, 부서, 개인 전화번호를 구별하여 저장함

이를 코드로 나타내면 다음과 같습니다.

//대학교 동창 친구들
class UnivFreind{
    private String name; //이름
    private String major; //전공
    private String phone; //번호

	//생성자
    public UnivFreind(String name, String major, String phone) {
        this.name = name;
        this.major = major;
        this.phone = phone;
    }

	//showInfo 메소드
    public void showIno(){
        System.out.println("이름 " +name);
        System.out.println("전공: " + major);
    }
}

//직장 동료분들
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;
    }

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

public class MyFreiends {
    public static void main(String[]args){
        //대학 동창의 관리를 위한 배열과 변수를 선언
        UnivFreind[] ufrns = new UnivFreind[5];
        int ucnt = 0;

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

        //대학 동창 친구들의 정보 저장
        ufrns[ucnt++] = new UnivFreind("LEE","Computer","010-333-555");
        ufrns[ucnt++] = new UnivFreind("SED","Electronics","010-222-444");

        //직장 동료분들의 정보 저장
        cfns[ccnt++] = new CompFriend("YOON","R&D 1", "02-123-999");
        cfns[ccnt++] = new CompFriend("PARK", "R&D 2","02-321-777");

        //대학교 동창 친구들의 정보를 출력함
        for(int i = 0;i<ucnt;i++){
            ufrns[i].showIno();
            System.out.println();
        }

	//직장 동료분들의 정보를 출력함
        for(int i = 0; i<ccnt;i++){
            cfns[i].showInfo();
            System.out.println();
        }
    }
}

 

출력 결과

이름 LEE
전공: Computer

이름 SED
전공: Electronics

이름: YOON
부서: R&D 1
전화: 02-123-999

이름: PARK
부서: R&D 2
전화: 02-321-777

 

분석

위의 코드를 자세하게 분석해봅시다.

  1. 대학 동창 친구들을 저장하는 배열과 직장 동료분들을 저장하는 배열이 각각 구별되어 있음을 확인할 수 있습니다.
//대학 동창의 관리를 위한 배열과 변수를 선언
UnivFreind[] ufrns = new UnivFreind[5];
int ucnt = 0;

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

 

🔥 대학 동창 친구들과 직장 동료들의 저장에 필요한 배열과 변수가 달라 구분하였습니다.

 

때문에 다음과 같이 2개의 for문을 이용해 각각 구별해서 출력해야 합니다.

				//대학교 동창 친구들의 정보를 출력함
        for(int i = 0;i<ucnt;i++){
            ufrns[i].showIno();
            System.out.println();
        }

				//직장 동료분들의 정보를 출력함
        for(int i = 0; i<ccnt;i++){
            cfns[i].showInfo();
            System.out.println();
        }

 

만약 다음과 같은 상황이 일어나면 어떻게 해야할까요?

“전화번호 중 특정 이름을 검색해서 출력하는 기능을 추가하고 싶다”

 🚨 이 경우 두 배열에서 따로 따로 모두 검색해야 하는 번거로움이 생기게 됩니다.
      또한 기능을 추가할 수록 저장과 출력에 필요한 배열의 수가 얼마나 더 늘어날지 모릅니다.

 

이러한 문제를 상속을 적용함으로써 보다 관리하기 쉽고 효율적인 코드로 변경할 수 있습니다.

 

상속을 사용하여 스마트폰에 저장된 전화번호를 출력하는 시스템을 개선한 코드

class Friend{
    protected String name; //이름 
    protected String phone; //전화번호

	//생성자
    public Friend(String name, String phone) {
        this.name = name;
        this.phone = phone;
    }
	
	//showInfo메소드
    public void showInfo(){
        System.out.println("이름: " +name);
        System.out.println("전화: " + phone);
    }
}

//대학교 동창 친구들
class UnivFriend extends Friend{
    private String major; //전공
	
	//생성자
    public UnivFriend(String name,String major,String phone){
        super(name, phone);
        this.major = major;
    }

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

//회사 직장 동료분들
class ComFriend extends Friend{
    private String department;

	//생성자
    public ComFriend(String name,String department,String phone){
        super(name, phone);
        this.department = department;
    }

	//showInfo 메소드
    public void showInfo(){
        super.showInfo();
        System.out.println("부서: " + department);
    }
}

public class MyFriends {
    public static void main(String[]args){
        Friend[] frns = new Friend[10];
        int cnt = 0;

        frns[cnt++] = new UnivFriend("LEE","Computer","010-333-555");
        frns[cnt++] = new UnivFriend("SEO","Electronics","010-222-444");
        frns[cnt++] = new ComFriend("YOON","R&D 1","02-123-999");
        frns[cnt++] = new ComFriend("PARK","R&D 2","02-321-777");

        //모든 대학교 동창 친구들 및 직장 동료분들의 정보 전체 출력
        for(int i = 0; i<cnt;i++){

	//각 클래스마다 오버라이딩한 메소드가 호출됩니다.
            frns[i].showInfo();
            System.out.println();
        }
    }
}

 

출력 결과

이름: LEE
전화: 010-333-555
전공: Computer

이름: SEO
전화: 010-222-444
전공: Electronics

이름: YOON
전화: 02-123-999
부서: R&D 1

이름: PARK
전화: 02-321-777
부서: R&D 2

 

분석

위의 코드에서 UnivFriend 클래스와 CompFriend 클래스가 Friend 클래스를 상속하였습니다.

//대학교 동창 친구들
class UnivFriend extends Friend{
		....
}

//회사 직장 동료분들
class ComFriend extends Friend{
			.....
}

 

이로인해 우리는 다음과 같은 장점을 얻을 수 있었습니다.

  1. 인스턴스를 저장하는 배열을 하나로 생성할 수 있었다.(정보를 저장하는 배열을 하나로 이용할 수 있었다.)

public class MyFriends {
    public static void main(String[]args){
	//대학 동창 친구들과 직장 동료분들을 저장하는 배열
        Friend[] frns = new Friend[10];
				....
}

 

  2. 저장된 정보를 모두 출력할 때 하나의 for문을 이용하였다.

        //모든 대학교 동창 친구들 및 직장 동료분들의 정보 전체 출력
        for(int i = 0; i<cnt;i++){

						//각 클래스마다 오버라이딩한 메소드가 호출됩니다.
            frns[i].showInfo();
            System.out.println();
        }

 

이로 인해 Friend 클래스는 UnivFriend 클래스와 CompFriend 클래스에 대해서 다음과 같이 말할 수 있습니다.

 🔥 Friend 클래스는 UnivFriend 클래스와 CompFriedn 클래스에 대한 공통된 규약들을 적용하기 위해 정의된 클래스입니다.

 

2. final 선언, 어노테이션 @Override


클래스와 메소드에 final 선언

이전 포스팅에서 변수에 final 선언을 붙이면 값을 변경할 수 없는 상수로 사용할 수 있다는 것을 살펴봤었습니다.

그런데 클래스와 메소드에 final 선언을 하면 어떻게 될까요?

 

❓클래스와 메소드에 final선언을 붙이게 되면?

🔥 클래스에 final 선언을 붙이면 “해당 클래스를 다른 클래스가 상속하는 것을 허용하지 않는다”는 의미입니다.
     메소드에 final 선언을 붙이면 “메소드의 오버라이딩을 허용하지 않는다”는 의미입니다.

 

이를 코드를 통해 살펴봅시다.

클래스에 final 선언을 한 경우

public final class MyLastCLS{ //MyLastCLS클래스는 다른 클래스가 상속할 수 없습니다.
		...
} 

 

메소드에 final 선언을 한 경우

class Simple{
	//func 메소드는 다른 클래스에서 오버라이딩 할 수 없습니다.
	public final void func(int n){
			...
	}
}

 

어노테이션 @Override


❓어노테이션 @Override란?

 🔥 자바 컴파일러에게 “해당 메소드는 오버라이딩을 하였음”을 알려주는 표시입니다.
      이를 통해 프로그래머가 미처 발견하지 못한 문법적 오류를 바로 잡을 수 있습니다.

 

  • 자바 5에서 어노테이션이 추가되었습니다.
  • 보통 메소드를 오버라이딩을 할때는 어노테이션 @Override를 붙입니다.
  • 프로그래머에게도 어노테이션 @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){
        System.out.println("덧셈을 진행합니다.");
        return  a+ b;
    }
}

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

 

출력 결과

7

 

분석

위의 코드에서 클래스는 ChidAddr는 ParentAdd 클래스를 상속하였습니다.

그리고 ParentAdd의 add메소드를 오버로딩 할려고 하였습니다.

class ChildAdder extends ParentAdder{
    // 상위 클래스의 add 메소드를 오버라이딩 하려고 합니다.
    public double add(double a, double b){
				....
}

 

그런데 메소드의 오버라이딩되지 않아도 컴파일과정을 거쳐 다음과 같이 출력됩니다.

출력 결과

7

 

프로그래머가 의도한 출력 결과

7.0

 

이유는 메소드의 오버라이딩이 성립하는 조건이 다르기 때문에 부모 클래스의 Add 메소드를 이용해 출력하게 되었습니다.

메소드 오버라이딩이 성립하는 조건

⭕  1. 메소드의 이름
      2.메소드의 반환형
      3. 메소드의 매개변수 선언
  • 메소드의 반환형과 매개변수 선언이 다르기 때문에 ChildAddr 클래스의 Add 메소드를 이용할 수 없습니다.

그런데 컴파일 과정에서 인식되지 않고 출력이 되었습니다.

이는 큰 프로그램에서 코딩한 프로그래머의 실수로 인해 프로그램에 치명적인 오류가 발생할 수 있습니다.
이때 어노테이션 @Override를 이용하면 해당 클래스가 오버로딩 되지 않는다는 것을 프로그래머에게 알려 줄 수 있습니다.

 

변경

class ChildAdder extends ParentAdder{

    @Override
    public double add(double a, double b){
        System.out.println("덧셈을 진행합니다.");
        return  a+ b;
    }
}

 

출력 결과

java: method does not override or implement a method from a supertype

 

메소드가 정상적인 메소드 오버로딩이 아니라는 것을 프로그래머에게 알려줍니다.

이 로그를 통해 프로그래머는 작성한 코드를 다시 한 번 살펴볼 수 있습니다.

 

📒 Reference (참고 자료)


  1. 윤성우의 열혈 자바
profile

제이제이

@아사비치즈스틱

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!