JAVA/이것이 자바다

Chapter .07 상속

개념원리 2021. 7. 17. 08:20

7.1 상속의 개념

- 현실 세계:

  • 부모가 자식에게 물려주는 행위

  • 부모가 자식을 선택해서 물려줌

 

- 객체 지향 프로그램:

  • 자식(하위, 파생) 클래스가 부모(상위) 클래스의 멤버를 물려받는 것

  • 자식이 부모를 선택해 물려받음 

  • 상속 대상: 부모의 필드와 메소드

 

현실에서 상속(Inheritance)은 부모가 자식에게 물려주는 행위를 말한다.

자식은 상속을 통해서 부모가 물려준 것을 자연스럽게 이용할 수 있다.

객체 지향 프로그램에서도 부모 클래스의 멤버를 자식 클래스에게 물려줄 수 있다.

 

프로그램에서는 부모 클래스를 상위 클래스라고 부르기도 하고, 자식 클래스를 하위 클래스 또는 파생클래스라고 부른다.

 

 

상속은 이미 잘 개발된 코드를 재사용해서 새로운 클래스를 만들기 때문에 코드의 중복을 줄여준다.

//부모 클래스 A 
public class A{
    int field1;
    void method(){...}
}
//자식 클래스
public class B extends A{
    String field2;
    void method2(){....}
}

■ 상속(Inheritance) 개념의 활용

- 상속의 효과

  • 부모 클래스 재사용해 자식 클래스 빨리 개발 가능

  • 반복된 코드 중복 줄임

  • 유지 보수 편리성 제공

  • 객체 다형성 구현 가능

 

- 상속 대상 제한

  • 부모 클래스의 private 접근 갖는 필드와 메소드 제외

  • 부모 클래스가 다른 패키지에 있을 경우, default 접근 갖는 필드와 메소드도 제외

 

7.2 클래스 상속

현실에서 상속은 부모가 자식을 선택해서 물려주지만, 프로그램에서는 자식이 부모를 선택한다.

자식 클래스를 선언할 때 어떤 부모 클래스를 상속받을 것인지를 결정하고 선택된 부모 클래스는 다음과 같이 extends 뒤에 기술한다.

 

extends 키워드

 - 자식 클래스가 상속할 부모 클래스를 지정하는 키워드

 

- 자바는 단일 상속

- 부모 클래스 나열 불가

하나의 클래스만 상속이 가능하다

■ 자식 객체 생성하면 부모 객체도 생성되는가?

  - 부모 없는 자식 없음

    • 자식 객체 생성할 때는 부모 객체부터 생성 후 자식 객체 생성

    • 부모 생성자 호출 완료 후 자식 생성자 호출 완료

 

■ 명시적인 부모 생성자 호출

  - 부모 객체 생성할 때, 부모 생성자 선택해 호출

super를 이용해서 부모 생성자를 호출한다

  • super(매개값,…)

    - 매개값과 동일한 타입, 개수, 순서 맞는 부모 생성자 호출

 

  • 부모 생성자 없다면 컴파일 오류 발생

 

  • 반드시 자식 생성자의 첫 줄에 위치

 

  • 부모 클래스에 기본(매개변수 없는) 생성자가 없다면 필수 작성

 

7.4 메소드 재정의

  - 부모 클래스의 상속 메소드 수정해 자식 클래스에서 재정의하는 것

 

부모 클래스의 모든 메소드가 자식 클래스에 맞게 설계되어 있다면 가장 이상적인 상속이지만, 어떤 메소드는 자식 클래스가 사용하기에 적합하지 않을 수도 있다.

이 경우 상속된 일부 메소드는 자식 클래스에서 다시 수정해서 사용해야 한다.

자바는 이런 경우를 위해 메소드 오버라이딩(Overriding) 기능을 제공한다.

 

- 메소드 재정의 조건 (p.295~296)

  • 부모 클래스의 메소드와 동일한 시그니처 가져야

 

  • 접근 제한을 더 강하게 오버라이딩 불가

    - public을 default나 private으로 수정 불가

    - 반대로 default는 public 으로 수정 가능

 

  • 새로운 예외(Exception) throws 불가 (예외처리는 10장 참조)

 

 

7.4.1 메소드 재정의(@Override)

- 컴파일러에게 부모 클래스의 메소드 선언부와 동일한지 검사 지시

- 정확한 메소드 재정의 위해 붙여주면 OK

 

■ 메소드 재정의 효과

  - 부모 메소드는 숨겨지는 효과 발생

    • 재정의된 자식 메소드 실행

 

메소드 오버라이딩은 상속된 메소드의 내용이 자식 클래스에 맞지 않을 경우, 자식 클래스에서 동일한 메소드를 재정의 하는 것을 말한다.

메소드가 오버라이딩 되어 있다면 부모 객체의 메소드는 숨겨지기 때문에, 자식 객체에서 메소드를 호출하면 오버라이딩된 자식 메소드가 호출된다.

 

 

 

7.4.2 부모 메소드 호출(super)

자식 클래스에서 부모 클래스으 ㅣ메소드를 오버라이딩 하게 되면, 부모 클래스의 메소드는 숨겨지고 오버라이딩된 자식 메소드만 사용된다.

 

그러나 자식 클래스 내부에서 오버라이딩 된 부모 클래스의 메소드를 호출해야 하는 상황이 발생한다면 명시적으로 super 키워드를 붙여서 부모 메소드를 호출 할 수 있다.

super.부모메소드;

 

7.5 final 클래스와 final 메소드

- final 필드: 수정 불가 필드

- final 클래스: 부모로 사용 불가한 클래스

- final 메소드: 자식이 재정의할 수 없는 메소드

 

final 키워드는 해당 선언이 최종 상태이고, 결코 수정될 수 없음을 뜻한다.

 

 

7.5.1 상속할 수 없는 final 클래스

클래스를 선언할 때 final 키워드를 class 앞에 붙이게 되면 최종적인 상태이므로 상속 할 수 없는 클래스가 된다.

 

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

메소드를 선언할 떄 final 키워드를 붙이게 되면 이 메소드는 최종적인 메소드이므로 오버라이딩(Overriding) 할 수 없는 상태가 된다.

 

 

7.6 protected 접근제한자

- 상속과 관련된 접근 제한자

  • 같은 패키지: default와 동일

  • 다른 패키지: 자식 클래스만 접근 허용 (p.303~305)

 

 

7.7 타입 변환과 다형성

 

다형성

- 같은 타입이지만 실행 결과가 다양한 객체 대입(이용) 가능한 성질

  • 부모 타입에는 모든 자식 객체가 대입 가능

     - 자식 타입은 부모 타입으로 자동 타입 변환

 

효과: 객체 부품화 가능

 

7.7.1 자동 타입 변환(Promotion)

- 프로그램 실행 도중에 자동 타입 변환이 일어나는 것

 

자동 타입 변환의 개념은 자식은 부모와 특징과 기능을 상속받기 때문에 부모와 동일하게 취급될 수 있다는 것이다.

예를들어 고양이는 동물의 특징과 기능을 상속받았다.

그래서 "고양이는 동물이다" 가 성립한다.

Animal과 Cat클래스가 다음과 같이 상속 관계에 있다고 해보자.

Cat클래스로 부터 Cat 객체를 생성하고 이것을 Animal 변수에 대입하면 자동으로 타입 변환이 일어난다.

cat과 animal 변수는 타입만 다를 뿐, 동일한 cat 객체를 참조한다.

 

 

 

 

- 바로 위의 부모가 아니더라도 상속 계층의 상위면 자동 타입 변환 가능

  • 변환 후에는 부모 클래스 멤버만 접근 가능 (p.308~310)

 

 

Class Parent{
    void method1(){....}
    void method2(){....}
}

Class Child extends Parent{
    void method2(){....} //override
    void method3(){....}
}
class ChildExample{
	public static void main(String[] args){
    	Child child = new Child();
        Parent parent = child;
    	
    	parent.method1(); //부모 메소드 호출
        
        parent.method2(); //자식 클래스 메소드 호출
        
        parent.method3(); // 호출 불가능
        
    }
}

Child 객체는 method3()의 메소드를 가지고 있지만, Parent 타입으로 변환된 이후에는 method3()을 호출 할 수 없다.

그러나 method2() 메소드는 부모와 자식에게 모두 있다. 이렇게 오버라이딩 된 메소드는 타입 변환 후에도 자식 메소드가 호출 된다.

 

 

7.7.2 필드의 다형성

다형성이란 동일한 타입을 사용하지만 다양한 결과가 나오는 성질을 말한다.

주로 필드의 값을 다양화함으로써 실행 결과가 다르게 나오도록 구현하는데, 필드의 타입은 변함이 없지만 실행 도중에 어떤 객체를 필드로 저장하느냐에 따라 실행 결과가 달라질 수 있다.

이것이 필드의 다형성이다.

 

자동차를 구성하는 부품은 언제든지 교체할 수 있다.

부품은 고장날 수도 있고 보다 더 성능이 좋은 부품으로 교체되기도 한다.

 

객체 지향 프로그램에서도 마찬가지이다.

프로글매은 수많은 객체들이 서로 연결되고 각자의 역할을 하게 되는데 이 객체들은 다른 객체로 교체될 수 있어야 한다.

class Car{
    Tire frontLeftTire = new Tire();
    Tire frontRightTire = new Tire();
    Tire backLeftTire = new Tire();
    Tire backRightTire = new Tire();


	void run(){.....}
}

Car 클래스는 4개의 tire 필드를 가지고 있다.

Car 클래스로부터 car객체를 생성하게 되면 Tire 필드에 각각 하나씩 Tire 객체가 들어가게 된다.

그런데 앞바퀴를 한국타이어로 뒷바퀴를 금호타이어로 교체할 필요성이 생겼다.

Car myCar = new Car();
myCar.frontRightTire = new HankookTire();
myCar.backLeftTire = new KumhoTire();
myCar.run();

 

 

7.7.3 하나의 배열로 객체 관리

이전 예제에서 Car 클래스에 4개의 타이어 객체를 4개의 필드로 각각 저장했다.

우리는 동일한 타입의 값들은 배열로 관리 할 수 있다.

 

7.7.4 매개 변수의 다형성

자동 타입 변환은 필드의 값을 대입할 떄에도 발생하지만, 주로 메소드를 호출할 떄 많이 발생한다.

메소드를 호출할 떄 매개 변수의 탕비과 동일한 매개값을 지정하는 것이 정석이지만, 매개값을 다양화하기 위해 매개 변수에 자식 타입 객체를 지정할 수도 있다.

 

예를들어 다음과 같이 Driver 클래스에는 drive() 메소드가 정의되어 있는데 vehicle 타입의 매개 변수가 선언되어 있다.

class Driver{
    void drive(Vehicle vehicle){
    	vehicle.run();
    }
}

drive 메소드를 정상적으로 호출 한다면 다음과 같을 것이다.

Driver driver = new Driver();
Vehicle vehicle = new Vehicle();

driver.drive(vehicle);

만약 Vehicle의 자식 클래스인 Bus 객체를 drive() 메소드의 매가값으로 넘겨준다면 어떻게 될까?

drive() 메소드는 Vehicle 타입을 매개 변수로 선언했지만, Vehicle을 상속받는 Bus 객체가 매개값으로 사용되면 자동 타입 변환이 발생한다.

 

매개 변수 타입이 클래스일 경우, 해당 클래스의 객체뿐만 아니라 자식 객체까지도 매개값으로 사용할 수 있다는 것이다.

매개값으로 어떤 자식 객체가 제공되느냐에 따라 메소드의 실행 결과는 다양해 질 수 있다.(매개 변수의 다형성)

자식 객체가 부모의 메소드를 재정의(오버라이딩)했다면 메소드 내부에서 오버라이딩된 메소드를 호출함으로써 메소드의 실행 결과는 다양해진다.

 

7.7.5 강제 타입 변환(Casting)

- 부모 타입을 자식 타입으로 변환하는

 

강제 타입 변환은 부모 타입을 자식 타입으로 변환하는 것을 말한다.그렇다고 해서 모든 부모 타입을 자식 클래스 타입으로 강제 변활할 수 있는 것은 아니다.자식 타입이 부모 타입으로 자동 변환 후, 다시 자식 타입으로 변환할 때 강제 타입 변환을 사용할 수 있다.

 

Class Parent{
    String file1;
    void method1(){....}
    void method2(){....}
}

Class Child extends Parent{
    String file2;
    void method3(){....}
}
class ChildExample{
	public static void main(String[] args){
    	Parent parent = new Child();
        parent.file1 = "ABC";
        
        parent.method1();
        parent.method2();
        
        parent.fild2 = "yyy";      // 사용 불가 
        parent.method3();          // 사용 불가
        
        Child child = (Child)parent;
        
        child.fild2 = "yyy";    
        child.method3();        
        
    }
}

 

 

7.7.6 객체 타입 확인(instanceof)

강제 타입 변환은 자식 타입이 부모 타입으로 변환되어 있는 경우만 가능하기 때문에 다음과 같이 부모 ㅏ입의 변수가 부모 객체를 참조할 경우 자식 타입으로 변환할 수 없다.

Parent parent = new Parent();
Child child = (Child)parent;     // 강제 타입 변환 불가

 

그래서 부모 변수가 참조하는 객체가 부모 객체인지, 자식 객체인지를 확인해야 한다.

객체가 어떤 클래스의 인스턴스인지를 확인 하려면 instanceof 연산자를 이용할 수 있다.

 

 

 

7.8 추상 클래스

7.8.1 추상 클래스의 개념

- 추상(abstract)

  • 실체들 간에 공통되는 특성을 추출한 것

    예1:  새, 곤충, 물고기à 동물 (추상)

    예2: 삼성, 현대, LG à 회사 (추상)

 

- 추상 클래스(abstract class)

  • 실체 클래스들의 공통되는 필드와 메소드 정의한 클래스

  • 추상 클래스는 실체 클래스의 부모 클래스 역할 (단독 객체 X)

 

 

 

7.8.2 추상 클래스의 용도

1) 실체 클래스의 공통된 필드와 메소드의 이름 통일할 목적

  • 실체 클래스를 설계자가 여러 사람일 경우,

  • 실체 클래스마다 필드와 메소드가 제각기 다른 이름을 가질 수 있음

 

2) 실체 클래스를 작성할 때 시간 절약

  • 실체 클래스는 추가적인 필드와 메소드만 선언

 

3) 실체 클래스 설계 규격을 만들고자 할 때

  • 실체 클래스가 가져야 할 필드와 메소드를 추상 클래스에 미리 정의

  • 실체 클래스는 추상 클래스를 무조건 상속 받아 작성

 

 

7.8.3 추상 클래스 선언

- 클래스 선언에 abstract 키워드 사용

  • New 연산자로 객체 생성하지 못하고 상속 통해 자식 클래스만 생성 가능

public abstract class 클래스{
    //필드
    //생성자
    //메소드
}

 

7.8.4 추상 메소드와 오버라이딩

추상 클래스는 실체 클래스가 공통적으로 가져야 할 필드와 메소드들을 정의해 놓은 추상적인 클래스이므로, 실체 클래스 멤버(필드, 메소드)를 통일화 하는데 목적이 있다.

 

예를들어 모든 동물은 소리를 내기 때문에 Animal 추상 클래스에 sound() 라는 메소드를 정의했다고 하자.

그렇다면 어떤 소리를 내도록 해야하는데 이것은 실체에서 작성해야 될 부분임을 알게 된다.

 

- 메소드 이름 동일하지만, 실행 내용이 실체 클래스마다 다른 메소드

  예: 동물은 소리를 낸다. 하지만 실체 동물들의 소리는 제각기 다르다.

 

- 구현 방법

  • 추상 클래스에는 메소드의 선언부만 작성 (추상 메소드)

  • 실체 클래스에서 메소드의 실행 내용 작성(오버라이딩(Overriding))