[Java] 클래스의 상속 (상속의 목적)

정보구니·2022년 1월 3일
0

Java

목록 보기
34/39
post-thumbnail

상속의 목적


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

이 문장의 의미를 코드 상에서 이해하기 위해 간단한 "인맥 관리 프로그램" 을 만들어보려고 한다.



🔎 프로그램을 통해 저장 및 관리할 대상

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



🔎 상속 전

상속 전 main 메소드를 보면 다음과 같은 사실을 알 수 있다.

  • 인스턴스를 저장하는 배열이 두 개(대학 동창, 직장 동료)다.
  • 정보를 저장하는 과정에서 대학 동창과 직장동료를 나누어 진행한다.
    저장에 필요한 배열과 변수가 다르기 때문
  • 저장된 정보를 모두 출력할 때 두개의 for문을 작성한다.
    출력에 사용되는 배열과 변수가 다르기 때문

이러한 방식의 프로그램은 배열의 수가 늘어날 수록 더욱 더 복잡해질 것이다.
즉, 프로그램의 확장성이 없다는 단점을 가진다.


/* 인맥관리 프로그램 (상속 전) */

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

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

class CompFriend { // 직장 동료
	private String name;
	private String phone;
	private String department;
	
	public CompFriend(String name, String phone, String department) {
		this.name = name;
		this.phone = phone;
		this.department = department; // 부서
	}
	
	public void showInfo() {
		System.out.println("이름: " + name);
		System.out.println("전화번호: " + phone);
		System.out.println("부서: " + department);
	}
}

class MyFriends {
	public static void main(String[] args) {
		// 대학 동창 관리를 위한 배열과 변수
		UnivFriend[] uf = new UnivFriend[5];
		int ucnt = 0;
		
		// 직장 동료 관리를 위한 배열과 변수
		CompFriend[] cf = new CompFriend[5];
		int ccnt = 0;
		
		// 대학 동창 정보 저장
		uf[ucnt++] = new UnivFriend("Lee", "010-222-333", "java");
		uf[ucnt++] = new UnivFriend("Kim", "010-444-555", "python");

		// 직장 동료 정보 저장
		cf[ccnt++] = new CompFriend("Lim", "010-555-666", "R&D 1");
		cf[ccnt++] = new CompFriend("Park", "010-777-888", "R&D 2");
		
		// 모든 동창 및 동료의 정보 전체 출력
		for (int i=0; i<ucnt; i++) {
			uf[i].showInfo();
			System.out.println();
		}
		for (int i=0; i<ccnt; i++) {
			cf[i].showInfo();
			System.out.println();
		}
	}
}


// 출력 결과
이름: Lee
전화번호: 010-222-333
전공: java

이름: Kim
전화번호: 010-444-555
전공: python

이름: Lim
전화번호: 010-555-666
부서: R&D 1

이름: Park
전화번호: 010-777-888
부서: R&D 2





🔎 상속 후

위와 같은 문제점을 상속으로 해결할 수 있다.

상속을 통해 대학 동창(UnivFriend)과 직장 동료(CompFriend)의 공통적인 규약을 정의할 수 있는 것이다.

상속 후 main 메소드를 보면 다음과 같은 사실을 알 수 있다.

  • 인스턴스를 저장하는 배열이 한 개다.
    Friend 클래스를 상속하는 클래스가 추가되어도 이 사실은 변함이 없다. ( 좋은 확장성 )
  • 정보를 저장하는 과정이 나뉘지 않는다.
    하나의 배열에 모든 인스턴스를 저장할 수 있다.
  • 저장된 정보를 모두 출력 할 때 하나의 for문으로 충분하다.
    하나의 배열이 사용되었고 메소드 오버라이딩을 활용했다.

/* 인맥관리 프로그램 (상속 후) */

class Friend {
	protected String name;
	protected String phone;
	
	public Friend(String na, String ph) {
		name = na;
		phone = ph;
	}
	
	public void showInfo() {
		System.out.println("이름: " + name);
		System.out.println("전화번호: " + phone);
	}
}

class UnivFriend2 extends Friend {
	private String major;
	
	public UnivFriend2(String na, String ph, String ma) {
		super(na, ph); // 상위클래스 생성자 호출
		major = ma;
	}
	
	public void showInfo() { // 메소드 오버라이딩
		super.showInfo();
		System.out.println("전공: " + major);
	}
}

class CompFriend2 extends Friend {
	private String department;
	
	public CompFriend2(String na, String ph, String de) {
		super(na, ph); // 상위클래스 생성자 호출
		department = de;		
	}
	
	public void showInfo() { // 메소드 오버라이딩
		super.showInfo();
		System.out.println("부서: " + department);
	}	
}


class MyFriends2 {
	public static void main(String[] args) {
		Friend[] f = new Friend[5];
		int cnt = 0;
		
		f[cnt++] = new UnivFriend2("Lee", "010-222-333", "java");
		f[cnt++] = new UnivFriend2("Kim", "010-444-555", "python");
		f[cnt++] = new CompFriend2("Lim", "010-555-666", "R&D 1");
		f[cnt++] = new CompFriend2("Park", "010-777-888", "R&D 2");
		
		// 모든 동창 동료 정보 전체 출력
		for (int i=0; i<cnt; i++) {
			f[i].showInfo(); // 오버라이딩 한 메소드가 호출됨
			System.out.println();
		}
	}

}


// 출력 결과
이름: Lee
전화번호: 010-222-333
전공: java

이름: Kim
전화번호: 010-444-555
전공: python

이름: Lim
전화번호: 010-555-666
부서: R&D 1

이름: Park
전화번호: 010-777-888
부서: R&D 2









object 클래스


// 다음 둘은 동일한 클래스 정의이다.
class Myclass {...}
class Myclass extends Object {...}
  • 클래스를 정의 할 때 어떤 클래스도 상속하지 않으면, 해당 클래스는 java.lang 패키지에 있는 Object 클래스를 상속하게 된다.

  • 자바의 모든 클래스는 Object 클래스를 직접 혹은 간접적으로 상속하게 되어있는 것이다.
    이유) 자바의 모든 인스턴스에 공통된 기준 및 규약을 적용하기 위함



🔎 관련 예제

다음 예제에서는 모든 클래스가 상속하는 Object 클래스의 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";
	}
}

class OverridingToStringExam {
	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@1c4af82c
My birthday cake

My birthday cheese cake









클래스 메소드의 final 선언


  • 클래스를 정의할 때 해당 클래스를 다른 클래스가 상속하는 것을 원하지 않는다면, final 선언을 추가하면 된다.

  • String 클래스는 대표적인 final 클래스에 해당한다.

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


// final 선언으로 인해, MyLastClass 클래스는 다른 클래스가 상속 할 수 없음
public final class MyLastClass {...}


// final 선언으로 인해, func 메소드를 다른 클래스에서 오버라이딩 할 수 없음
class MyClass {    
    public final void func(int n) {...}
}









@Override


  • 어노테이션 : 자바 컴파일러에게 메시지를 전달하는 목적의 메모이다.
  • 메소드 오버라이딩을 할 경우, 어노테이션의 일종인 @Override를 사용해 컴파일 과정에서 확인 되지 않는 오류의 발생을 차단하는 것이 좋다.
  • @Override는 컴파일러에게 '이 메소드는 부모 클래스의 메소드를 오버라이딩 할 목적으로 정의하였습니다.' 라는 메시지를 전달한다.
  • 컴파일러는 오버라이딩이 잘 되었는지 확인하고, 오버라이딩이 잘 되지 않았을 경우 컴파일 과정에서 다음 과 같은 에러 메시지를 전달해준다.
    error: method does not override of implement a method from a supertype









참고
열혈 자바 ch16 클래스의 상속3

0개의 댓글