상속 (Inheritance)

김찬희·2023년 2월 21일
0

KH정보교육원

목록 보기
8/27

▶ 상속

다른 클래스가 가지고 있는 멤버(필드, 메소드)들을 새로 작성할 클래스에서 직접 만들지 않고 상속을 받음으로써 새 클래스가 자신의 멤버처럼 사용할 수 있는 기능
→ 부모 클래스가 가지고 있는 필드/메서드를 자식 클래스가 물려 받아 자신의 것처럼 사용
√ 상속의 목적
클래스의 재사용, 연관된 클래스들에 대한 공통적인 규약 정의
√ 상속의 장점
1. 보다 적은 양의 코드로 새로운 클래스 작성 가능
2. 코드를 공통적으로 관리하기 때문에 코드의 추가 및 변경 용이
3. 코드의 중복을 제거하여 프로그램의 생산성과 유지보수에 크게 기여
√ 방법
클래스 간의 상속 시에는 extends(확장하다) 키워드 사용
√ 표현식

[접근제한자] class 클래스명 extends 클래스명 {}
public class Academy extends Company {}

▶ 단일 상속과 다중 상속

√ 단일 상속(Single Inheritance)
클래스간의 관계가 다중 상속보다 명확하고 신뢰성 있는 코드 작성
자바에서는 다중 상속 미지원 → 단일상속만 지원
√ 다중 상속(Multiple Inheritance)
C++에서 가능한 기능으로 여러 클래스로부터 상속을 받으며 복합적인 기능을 가진 클래스를 쉽게 작성 가능
서로 다른 클래스로부터 상속 받은 멤버 간의 이름이 같은 경우 문제 발생
자바는 클래스간의 다중 상속 불가

▶ 상속의 특징

  1. 모든 클래스는 Object클래스의 후손
    Object클래스가 제공하는 메소드를 오버라이딩하여 메소드 재구현 가능
    ex) java.lang.String 클래스의 equals()와 toString()
  2. 부모클래스의 생성자, 초기화 블록은 상속 안 됨
    자식 클래스 생성 시, 부모 클래스 생성자가 먼저 실행
    자식 클래스 생성자 안에서 부모 클래스 생성자 호출을 명시하고 싶으면 super() 활용
  3. 부모의 private멤버는 상속은 되지만 직접 접근 불가
    자식 객체 생성 시에 부모의 필드 값도 전달 받은 경우, 자식 생성자 안에서 부모의 private 필드에 직접 접근하여 대입 불가
    super() 이용하여 전달받은 부모 필드 값을 부모 생성자 쪽으로 넘겨서 생성하거나 setter, getter 메소드를 이용하여 접근

▶ super()와 super.

√ super()
부모 객체의 생성자를 호출하는 메서드로 기본적으로 후손 생성자에 부모 생성자 포함
후손 객체 생성 시에는 부모부터 생성이 되기 때문에 후손 클래스 생성자 안에는 부모 생성자를 호출하는 super()가 첫 줄에 존재(부모 생성자가 가장 먼저 실행되어야 하기 때문에 명시적으로 작성 시에도 반드시 첫 줄에만 작성)
매개변수 있는 부모 생성자 호출은 super(매개변수, 매개변수)를 넣으면 됨
√ super.
상속을 통한 자식 클래스 정의 시 해당 자식 클래스의 부모 객체를 가리키는 참조변수
자식 클래스 내에서 부모 클래스 객체에 접근하여 필드나 메소드 호출 시 사용
super : 상위(부모)
sub : 하위(자식)

▶ 오버라이딩 (Overriding)

자식 클래스가 상속 받은 부모 메소드를 재작성 하는 것
부모가 제공하는 기능을 후손이 일부 고쳐 사용하겠다는 의미로 자식 객체를 통한 실행 시 후손 것이 우선권을 가짐
√ 특징
//, /**/ : 사람을 위한 주석
@ (Annotation) : 컴퓨터를 위한 주석
메서드 헤드라인 위에 반드시 Annotation(== comment==주석), @Override 표시
접근 제어자를 부모 것보다 같거나 넓은 범위로 변경 가능
부모 메서드의 예외처리 클래스 처리범위보다 좁은 범위로 예외처리 클래스 수정 가능
√ 성립 조건
부모 클래스의 메서드와 자식 클래스의 메서드 비교

  • 메소드 이름 동일
  • 매개변수의 개수, 타입, 순서 동일
  • 리턴 타입 동일
    ※ 접근제한자 빼고 나머지 메서드 선언부 동일
  • private 메서드 오버라이딩 불가
  • 접근 제어자는 부모와 같거나 넓은 범위로 변경 가능
  • 예외처리는 부모와 같거나 좁은 범위로 예외처리 변경 가능

▶ 오버로딩 (Overloading)

한 클래스 내에서 같은 이름의 메서드를 여러 개 정의하는 것
√ 성립 조건
같은 메서드 이름
다른 매개변수 선언부(매개변수 타입, 개수, 순서)
√ 주의 사항
메서드의 리턴타입은 오버로딩 조건과 관계 없음

▶ 오버라이딩(Overriding)과 오버로딩(Overloading)

▶ final 예약어

√ final 클래스
상속이 불가능한 클래스

public final class finalClass {}

√ final 메서드
상속 시 오버라이딩이 불가능한 메서드

public final void method() {}

▶ 대상에 따른 사용 가능한 제어자, 예약어


√ 유의 사항
클래스에 abstract와 final 동시에 사용 불가능
메서드에 static과 abstract 동시에 사용 불가능
abstract 메서드의 접근제어자로 private 불가능

package edu.kh.inheritance.dto;

// final class : 상속이 불가능한 클래스
//               보통 코드 재사용을 원치 않을 때 사용
public /*final*/ class Parent /*extends Object*/{
			   	 			// 컴파일러가 자동 추가
	
	private int money = 400_000_000;
	private String lastName = "박";  // 성
	
	// 기본 생성자
	public Parent() {
		System.out.println("Parent() 기본 생성자");
	}
	 
	// 매개변수 생성자
	public Parent(int money, String lastName) {
		this.money = money;
		this.lastName = lastName;
		
		System.out.println("Parent(int, String) 매개변수 생성자");
	}
	
	// getter / setter
	
	// final 메서드 : 오버라이딩 불가
	public /*final*/ int getMoney() {
		return money;
	}
	public void setMoney(int money) {
		this.money = money;
	}
	
	public String getLastName() {
		return lastName;
	}
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
	
	public String toString() {
		return money + " / " + lastName;
	}
}
package edu.kh.inheritance.dto;
                   // 확장하다 (Parent의 코드를 상속받아 Child1 확장)
public class Child1 extends Parent{

	private String car;
	
	// 생성자
	public Child1() {
		super();  // super() 생성자
		System.out.println("Child1() 기본 생성자");
	}
	
	// 매개변수 생성자
	public Child1(String car) {
//		super();  // super() 생성자
				  // → 자식 객체 생성 시 부모 객체를 먼저 생성하게 함
				  // ** 미작성 시 컴파일러가 자동 추가 **
				  
		// 부모로부터 상속받은 setter를 이용해서 부모필드 초기화
		// setMoney(100_000_000);
		// setLastName("김");
		
		// 부모의 필드를 초기화 하는 방법이 부모 매개변수 생성자에 존재
		// → 이를 호출해서 사용 (코드길이 감소, 재사용성 증가)
		super(200_000_000, "백");
		
		this.car = car;
		System.out.println("Child1(String) 매개변수 생성자");
	}

	// getter / setter
	public String getCar() {
		return car;
	}

	public void setCar(String car) {
		this.car = car;
	}
	
	public String toString() {
//		Parent p1 = new Parent();
//		p1.getMoney();
		// 자신의 메서드(같은 클래스) 호출 시 이름만 부르면 된다!
		// + 상속 특징(부모 필드/메서드를 상속 받아 자식 것처럼 사용)
//		System.out.println(getCar());
//		System.out.println(getMoney());
//		System.out.println(getLastName());
		
		// 부모의 필드 값을 간접 접근 방법으로 얻어와 하나의 문자열로 만들어 반환
//		return car + " / " + getMoney() + " / " + getLastName();
		
		// 문제점 : StackOverflowError 발생
		// 원인 : Child1의 toString() 호출 시
		//		  같은 toString()은 계속 반복해서 호출(== 재귀 호출)
		// return car + " / " + toString();
		
		// 해결 방법 : 부모의 toString() 호출을 명시 → super. 참조변수 이용
		return car + " / " + super.toString();
	}
}
package edu.kh.inheritance.dto;

public class Child2 extends Parent{

	private String house;
	
	public Child2() {
		System.out.println("Child2() 기본 생성자");
	}
	
	public Child2(String house) {
		this.house = house;
		System.out.println("Child2(String) 매개변수 생성자");
	}
	
	public String getHouse() {
		return house;
	}
	public void setHouse(String house) {
		this.house = house;
	}
	
	// 부모의 toString() 존재
	/*
	public int getMoney() {
		return money;
	}
	*/
	// 자식이 상속 받은 getMoney()을 다시 작성(재정의)
	// @Override : 컴파일러에게 해당 메서드는 재정의 되었다는 것을
	//             알려주는 컴퓨터용 주석
	//             → 오버라이딩 형식이 맞는지 검사 진행
	
	@Override
	public int getMoney() {
		System.out.println("오버라이딩 한 getMoney()");
		return super.getMoney() + 500;
		// 부모의 getMoney() 반환값에 500원 추가
	}
}
package edu.kh.inheritance.run;

import edu.kh.inheritance.dto.Child1;
import edu.kh.inheritance.dto.Child2;
import edu.kh.inheritance.dto.Parent;

public class TestRun {

	public static void main(String[] args) {
		// 부모, 자식1,2 객체 생성
		Parent p = new Parent(1_000_000_000, "김");
		
		Child1 c1 = new Child1("포르쉐");
		Child2 c2 = new Child2("시그니엘");
		
		
		
		// 상속(extends)
		// - 부모 클래스가 가지고 있는 필드, 메서드를
		//   자식 클래스가 자신의 것처럼 사용 가능하게 하는 기술
		
		System.out.println(c1.getMoney());
		System.out.println(c1.getLastName());
		
		// 메서드 오버라이딩 시 자식이 우선 순위를 가지게 된다!+
		System.out.println(c2.getMoney());
		System.out.println(c2.getLastName());
		// → 부모의 메서드 상속 확인
		
//		System.out.println(c1.money);
		// → 부모의 private 접근 제한자는 상속을 받아도 직접 접근 불가능
		// → private 필드/메서드는 상속이 되어 있음
		//   단, 자식도 접근만 못할 뿐이다 (private는 같은 클래스만 접근 가능)
		
		// 부모 클래스의 코드를 수정하면 
		// 자식 모두에게 적용됨(공통적인 규약, 유지보수성 향상)
		
		System.out.println("----------------------------");
		// toString : 객체가 가지고 있는 필드를 하나의 문자열로 만들어서 반환
		System.out.println(c1.toString());
	}
}
profile
김찬희입니다.

0개의 댓글