Search

Chapter 7. 상속

Created
2021/04/07 16:58
Tags

상속 개념

현실에서 상속은 부모가 자식에게 재산을 물려주는 행위
객체 지향 프로그램에서 부모 클래스의 멤버를 자식 클래스에게 물려줄 수 있다.
부모 클래스 = 상위 클래스
자식 클래스 = 하위 클래스, 파생 클래스
상속의 이점
1.
이미 개발된 클래스를 사용하여 새로운 클래스를 만들기 때문에 코드의 중복을 줄인다.
private 접근 제한자로 선언된 멤버는 상속되지 않는다.
default 접근 제한자로 선언된 멤버는 다른 패키지에 존재하는 자식 클래스에게 상속되지 않는다.
2.
클래스의 수정을 최소화하여 유지 보수 시간을 단축한다.
부모 클래스의 수정이 모든 자식 클래스에게 동일한 효과를 끼친다.

클래스 상속

클래스를 생성할 때 상속받을 부모 클래스를 선택한다.
public class '자식클래스' extends '부모클래스' { // 필드, 생성자, 메소드 }
Java
복사
오직 하나의 부모 클래스로부터 상속 받을 수 있다. → 다중 상속 미허용

부모 생성자 호출

자식 클래스로부터 객체를 생성하면, 우선 부모 객체가 먼저 생성된다.
생성자 호출 순서 : 자식 객체 생성자 호출 → 부모 객체 생성자 호출 → 부모 객체 생성 → 자식 객체 생성
public class '자식클래스' extends '부모클래스' { public 자식클래스생성자() { super(); // 부모의 생성자 호출 } }
Java
복사
생성자를 명시하지 않은 경우, 컴파일러가 자동으로 기본 생성자 생성 → 부모 클래스의 기본 생성자 호출
생성자를 명시하는 경우, 자식 클래스의 생성자 첫 줄에 부모 클래스의 생성자를 호출하는 super(매개값, ...); 를 호출해야 한다.
부모 클래스의 생성자 매개변수 타입과 일치하는 생성자를 명시 → 불일치시 컴파일 에러 발생

메소드 재정의

부모 클래스에 정의된 메소드를 자식 클래스에서 재정의하여 다양한 기능 수행 → 메소드 오버라이딩

메소드 재정의(@Override)

메소드 오버라이딩 : 부모 클래스에서 정의된 메소드를 자식 클래스에서 재정의 하는 것
메소드가 재정의되면 부모 객체의 원본 메소드는 숨겨지고 자식 객체의 재정의된 메소드가 호출된다.
메소드 오버라이딩 규칙
1.
부모 클래스의 메소드와 동일한 시그니처( 리턴 타입, 메소드 이름, 매개 변수 타입 )를 가져야 한다.
2.
접근 제한을 더 강하게 오버라이딩 할 수 없다. ( public < protected < default < private )
3.
새로운 예외를 throw 할 수 없다.
'@Override' 어노테이션 사용시, 정확히 오버라이딩 되었는지 컴파일러가 검사한다. (생략 가능)
class Parent { void method1(매개값); } class Child extends Parent { @Override public void method1(매개값){ // 재정의 } }
Java
복사

부모 메소드 호출(super)

자식 클래스에서 오버라이딩한 부모 클래스의 메소드를 호출할 때 super 키워드를 붙여서 호출할 수 있다.
super.부모메소드(); // 'super'는 부모 객체를 참조하고 있다.
Java
복사

final 클래스와 final 메소드

final 필드는 더 이상 값이 변경될 수 없음을 의미한다. → 클래스와 메소드에도 적용 가능

상속할 수 없는 final 클래스

final 클래스 선언 시 해당 클래스로부터 상속할 수 없다.
public final class '클래스' { // 필드, 생성자, 메소드 ... }
Java
복사
예 ) 자바 표준 API에서 제공하는 'String' 클래스

오버라이딩 할 수 없는 final 메소드

final 메소드 선언 시 자식 클래스가 해당 메소드를 오버라이딩 할 수 없다.
public final void '메소드'(매개값) {...}
Java
복사

protected 접근 제한자

protected : 같은 패키지의 클래스 혹은 다른 패키지의 자식 클래스에서 접근 가능
필드, 생성자, 메소드에 적용 가능하다.
단, 다른 패키지의 자식 클래스에서 부모 클래스의 protected로 선언된 생성자를 호출 시 'new' 연산자가 아닌 'super()' 를 사용해야한다.

타입 변환과 다형성

다형성 : 같은 타입이지만 실행 결과가 다양한 객체를 이용할 수 있는 성질
자바는 부모 클래스로 타입 변환을 허용한다. → 부모 타입에 자식 객체를 대입할 수 있다.
public class Car { // 'HankookTire'와 'KumhoTire' 클래스는 Tire 클래스의 자식 클래스 Tire t1 = new HankookTire(); Tire t2 = new KumhoTire(); }
Java
복사
기본 타입의 변환 : 데이터 타입을 다른 데이터 타입으로 변환하는 행위. ( byte → int )
클래스 타입의 변환 : 상속 관계에 있는 클래스 사이에서 발생하는 타입 변환 행위. 자식 타입 → 부모 타입으로 자동 타입 변환이 가능하다.

자동 타입 변환(Promotion)

자동 타입 변환 : 프로그램 실행 도중에 자동적으로 타입 변환이 일어나는 것
자식 클래스의 객체를 부모 클래스 타입 변수에 대입하면 자동 타입 변환이 발생한다.
부모클래스 변수 = 자식클래스타입; Cat cat = new Cat(); Animal animal = cat;
Java
복사
참조 타입 변수 'cat'과 'animal'은 타입만 다를 뿐, 동일한 객체를 가리키고 있다.
상속 관계에서 상위 타입이라면 자동 타입 변환이 일어날 수 있다.
부모 타입으로 자동 타입 변환이 일어난 이후에는 부모 클래스에 선언된 필드와 메소드만 사용 가능
단, 자식 클래스에서 오버라이딩된 메소드가 있는 경우 자식의 오버라이딩된 메소드가 호출된다.

필드의 다형성

자식 타입으로 사용하면 되는데 부모 타입으로 변환해서 사용하는 이유 → 다형성을 구현하는 기술적 방법
다형성 : 동일한 타입을 사용하지만 다양한 결과가 나오는 성질
필드의 타입은 변함이 없지만 어떤 객체를 저장하느냐에 따라서 실행 결과가 달라질 수 있다.
상속, 오버라이딩, 자동 타입 변환을 사용하여 다양한 결과를 얻을 수 있다.
class Car { Tire front = new Tire(); Tire back = new Tire(); void run() { front.roll(); // Tire 클래스의 roll() 메소드 호출 back.roll(); } }
Java
복사
Car car = new Car(); // HankookTire 와 KumhoTire는 Tire의 자식 클래스 car.front = new HankookTire(); // 자동 타입 변환 car.back = new KumhoTire(); // 자동 타입 변환 car.run(); // 재정의된 roll() 메소드 호출 // run() 메소드를 수정하지 않고 필드값을 교체하여 다양한 실행 결과를 얻을 수 있다.
Java
복사

하나의 배열로 객체 관리

필드를 여러 개 선언하여 객체 저장 하나의 배열에 여러 개의 객체 저장
Tire[] tires = { new Tire("front"), new Tire("back") }
Java
복사
코드가 간결하고 관리하기 편하다.

매개 변수의 다형성

메소드 호출 시 매개 변수로 자식 타입 객체를 넘겨줄 수 있다.
class Driver { void drive(Vehicle vehicle){ vehicle.run(); } }
Java
복사
Driver driver = new Driver(); Vehicle vehicle = new Vehicle(); Bus bus = new Bus(); driver.drive(vehicle); // 일반적인 경우 driver.drive(bus); // 자식 타입 객체 넘김
Java
복사
매개 변수의 타입이 클래스인 경우, 해당 클래스와 자식 객체를 매개값으로 사용할 수 있다.
매개값으로 어떤 객체가 넘어오느냐에 따라서 다양한 실행 결과를 얻을 수 있다.
자식 객체에서 메소드 오버라이딩을 한 경우

강제 타입 변환(Casting)

강제 타입 변환 : 부모 타입을 자식 타입으로 변환하는 것
단, 자식 타입이 부모 타입으로 자동 타입 변환이 일어난 후에만 가능하다.
자식 타입이 부모 타입으로 자동 변환하면 부모 타입에 선언된 필드와 메소드만 사용 가능
만약 자식 타입에 선언된 필드와 메소드를 사용해야 하는 경우 강제 타입 변환을 한다.
자식클래스 변수 = (자식클래스) 부모클래스타입;
Java
복사

객체 타입 확인(instanceof)

instanceof : 해당 객체가 어떤 클래스의 인스턴스인지 확인한다.
boolean result = '객체' instanceof '클래스';
Java
복사
자식 타입 → 부모 타입 자동 타입 변환이 일어난 경우에만 부모 타입 → 자식 타입으로 강제 타입 변환 가능
부모 변수가 참조하는 객체가 부모 객체인지 자식 객체인지 확인하는 방법 → 자동 타입 변환 확인
public void method(Parent parent) { if(parent instanceof Child) { Child child = (Child) parent; child.walk(); } }
Java
복사

추상 클래스

추상 : 실제간에 공통되는 특성들을 추출한 것
개, 고양이, 새 → 동물
실체 클래스 : 객체를 생성할 수 있는 클래스
추상 클래스 : 클래스들의 공통적인 특성을 추출해서 선언한 클래스
실체 클래스와 추상 클래스는 상속 관계
실체 클래스(자식)는 추상 클래스(부모)의 특성을 물려받고 추가적인 특성을 가질 수 있다.
추상 클래스 특징
'new' 연산자를 사용하여 객체를 생성할 수 없다.
오직 부모 클래스로만 사용된다.
class '실체클래스' extends '추상클래스' { // 필, 생, 메 }
Java
복사

추상 클래스의 용도

실체 클래스들의 공통된 필드와 메소드의 이름을 통일할 목적
실체 클래스를 설계하는 사람마다 필드와 메소드의 이름을 다르게 선언한다.
추상 클래스를 상속함으로써 공통된 필드와 메소드의 이름을 통일
실체 클래스를 작성할 때 시간을 절약
설계자가 설계한 클래스를 코더가 작성하는 경우, 추상 클래스를 전달하고 코더는 해당 클래스를 상속받아 구체적인 클래스를 구현

추상 클래스 선언

abstract : 추상 클래스 선언에 사용되는 키워드
public abstract class '추상클래스' { // 필, 생, 메 }
Java
복사
일반 클래스와 마찬가지로 필드, 생성자, 메소드를 선언할 수 있다.
추상 클래스를 상속받은 자식 객체가 생성될 때, 'super(매개값)' 를 호출해서 추상 클래스 객체를 생성
추상 클래스도 생성자가 있어야 한다.

추상 메소드와 오버라이딩

추상 클래스는 실체 클래스가 공통적으로 가져야 할 필드와 메소드를 정의 → 실체 클래스의 멤버의 통일
메소드의 선언만 통일화 하고, 실행 내용은 실체 클래스마다 달라야 하는 경우
추상 클래스에서 추상 메소드를 선언
추상 메소드는 추상 클래스에서만 선언할 수 있으며, 메소드의 선언부만 존재하고 중괄호가 없다.
추상 클래스의 자식 클래스에서 직접 추상 메소드의 실행 내용을 작성하도록 강제하는 효과
추상 메소드를 오버라이딩하지 않으면 컴파일 에러 발생
public abstract 리턴타입 메소드명(매개변수);
Java
복사
// Animal.java public abstract class Animal { // 추상 메소드 선언 public abstract void sound(); }
Java
복사
// Dog.java public class Dog extends Animal { @Override public void sound() { System.out.println("Bow Wow"); } }
Java
복사
// Cat.java public class Cat extends Animal { @Override public void sound() { System.out.println("Meow"); } }
Java
복사
// AnimalExample.java public class AnimalExample { public static void main(String[] args){ // 일반적인 객체 생성 및 메소드 호출 Dog dog = new Dog(); Cat cat = new Cat(); dog.sound(); cat.sound(); // 자동 타입 변환 Animal animal = null; animal = new Dog(); animal.sound(); // 부모 타입의 매개 변수에 자식 객체를 대입 -> 메소드의 다형성 animalSound(new Cat()); } public static void animalSound(Animal animal) { animal.sound(); } }
Java
복사