Let's kick it
[혼공자]4주차 - chap07 본문
7장. 상속 (inheritance)
7 - 1 상속
상속은 이미 잘 개발된 클래스를 재사용해 새로운 클래스를 만들기 때문에 중복되는 코드 사용을 줄여줘 효율적이고 개발 시간을 줄여준다. 상속을 이용하면 부모 클래스의 수정으로 모든 자식 클래스들도 수정되는 효과를 가져오기 때문에 유지 보수 시간을 최소화할 수 있다.
- 클래스 상속 : 현실에서는 부모가 재산을 물려줄 자식을 선택하지만, 자바에서는 자식이 부모를 선택한다. 내가 어떤 부모의 내용을 상속받아야겠구나라고. 자식은 상속을 통해 부모가 물려준 것을 자연스럽게 이용가능하다. extends 키워드 사용하고 뒤에 부모 클래스를 기술하면 된다.
class 자식 클래스 extends 부모 클래스 {
//필드
//생성자
//메소드
}
그러면 자식 클래스는 부모가 가지고 있는 필드나 메소드를 상속받음. 추가적으로 자신만이 가지고 있는 필드와 메소드를 작성할 수 있다.
상속의 특징으로 부모 클래스 단일 상속만 가능하고, 부모 클래스에서 private 접근 제한 갖는 필드와 메소드는 상속 대상에서 제외된다. private 접근 제한자는 그 클래스 내에서만 사용할 수 있다.
( 부모와 자식 클래스가 다른 패키지에 존재할 경우, default 접근 제한된 필드와 메소드 역시 제외된다. 왜냐면 default는 같은 패키지에 있는 클래스만 사용가능하기 때문에. 그리고 void는 접근 제한자가 붙지 않아 패키지가 다른 클래스에서는 사용불가. 상속의 대상자는 pubilc 접근 제한자를 갖고 있는 대상이다. )
상속에서 잘못 생각할 수 있는 부분이 자식 객체를 생성할 때 자식 객체만 생성되는 것처럼 보이지만, 사실은 부모 객체도 생성된다. 현실에서 부모 없는 자식 없는 것처럼 부모가 있어야 한다는 소리. 실질적으로는 메모리에 부모 객체가 먼저 생성되고 그다음 자식 객체가 생성된다.
6장에서 생성자의 호출이 있어야 객체를 생성할 수 있다고 배웠는데, 이 부모 객체가 생성될 때에도 부모 생성자가 호출되었다는 건데 자식 생성자의 맨 첫 줄에서 부모 생성자가 호출된다.
🤔 (생성자는 new 연산자로 클래스로부터 객체를 생성할 때 호출되어 객체 초기화를 담당. 클래스 이름으로 되어 있고, 리턴 타입이 없다.)
- 메소드 재정의 : 부모 클래스의 메소드를 자식 클래스에서 재정의하는 것을 말한다.
메소드를 재정의할 때 규칙
- 부모의 메소드와 동일한 시그니처(리턴 타입, 매개변수목록, 메소드 이름)를 가져야 한다.
- 접근 제한을 더 강하게 할 수 없다.
- 새로운 예외(Exception)을 throws 할 수 없다.
🧐 annotation(어노테이션) : 자바 소스 코드에 추가하여 사용할 수 있는 메타 데이터의 일종. 클래스 파일에 임베디드 되어 컴파일러에 의해 생성된 후 자바가상머신(JVM)에 포함되어 작동한다. 컴파일러에게 코드 작성 방법을 알려주거나, 컴파일 시간 및 실행 시간에 해석될 수 있는 정보를 제공한다. 어노테이션을 통해 코드의 의도를 명확히 할 수 있고, 오류 가능성을 줄일 수 있다. ex) @Override는 내장 어노테이션으로, 메소드가 오버라이딩 하는 것이라고 알린다.
- final 클래스와 final 메소드
final 클래스 : 최종적인 클래스라는 뜻으로 상속이 되지 않아 부모 클래스가 될 수 없어 자식 클래스를 만들지 못한다.
final 메소드 : 최종적인 메소드라는 뜻으로 재정의가 되지 않는 메소드이다. 상속해서 자식 클래스를 선언할 때 부모 클래스에서 정의된 메소드를 자식 클래스에서 재정의할 수 없다.
7 - 2 타입 변환과 다형성
기본 타입과 마찬가지로 클래스도 타입 변환이 있다. 이를 활용하면 객체 지향 프로그래밍의 다형성을 구현할 수 있다.
다형성 : 사용 방법은 동일하지만 다양한 객체를 활용해 여러 실행결과가 나오도록 하는 성질이다. 메소드 재정의 와 타입 변환으로 구현한다. 둘을 결합하면 다형성을 만들 수 있다.
메소드 재정의 + 타입 변환 -> 다형성
- 자동 타입 변환(promotion) : 프로그램 실행 도중 자동으로 타입 변환이 일어나는 것.
부모 타입의 변수 = 자식 타입;
자식 타입 객체를 부모 타입 변수에 대입할 때 자동 타입 변환이 생긴다. 조건이 부모와 자식이어야 한다.
그리고 바로 위 부모가 아니더라도 상속 계층에서 상위 타입인 경우 자동 타입 변환이 일어날 수 있다.
하지만 상속관계에 있을 때만 상의 타입으로 하위 객체에 대입이 된다.
부모 타입으로 자동 타입 변환되고 부모 타입의 변수를 가지고 호출 가능한 메소드를 살펴보면 부모 클래스에 선언된 피드 및 메소드만 접근 가능하다. 예외적으로, 메소드가 자식 클래스에서 재정의될 경우 자식 클래스의 메소드가 대신 호출된다.
- 필드의 다형성
필드에 부모 타입을 선언해 놓고 다양한 자식 객체를 대입해서 메소드를 호출하게 되면 다양한 실행 결과가 나온다.
- 매개변수의 다형성
자식 클래스로부터 생성된 객체들을 매개변수에 대입함으로써 매개변수를 부모 타입으로 선언하는 효과로, 메소드 호출 시 매개값으로 부모 객체 및 모든 자식 객체를 제공할 수 있다. 자식 클래스에서 재정의된 메소드가 호출함으로써 어떤 객체가 대입함에 따라 실행결과가 다르게 나오는 게 이것이 다형성.
- 강제 타입 변환(casting) : 부모 타입을 자식 타입으로 변환하는 것을 말한다. 조건은 자식 타입이 부모 타입으로 자동 타입 변환 후 다시 반대로 변환할 때 사용할 수 있다.
자식타입 변수 = (자식타입) 부모타입;
Parent parent = new Child(); //자동 타입 변환
Child child = (Child) parent; //강제 타입 변환
부모 타입을 자식 타입 변수에 대입하고 싶은데 그냥은 안되고 자식 타입으로 강제 타입 변환을 해서 대입할 수 있다.
자식 타입이 부모 타입으로 자동 타입 변환하면, 부모 타입의 부모에 선언된 필드와 메소드만 사용가능하다는 제약사항이 따른다. 만약 자식에 선언된 필드와 메소드를 꼭 사용해야 한다면 강제 타입 변환을 해서 다시 자식 타입으로 변환한 다음 자식의 필드와 메소드를 사용하면 된다.
- 객체의 타입 변환
강제 타입 변환은 처음부터 부모 타입으로 생성된 객체는 자식 타입으로 변환할 수 없다. 그렇다면 부모 변수가 참조하는 객체가 부모 객체인지 자식 객체인지 확인하는 방법은 어떤 객체가 어떤 클래스의 인스턴스인지 확인하기 위해 instanceof 연산자를 사용한다. instanceof 연산자의 좌항에는 객체가 오고, 우항에는 타입이 오는데, 좌항의 객체가 우항의 인스턴스이면, 즉 우항의 타입으로 객체가 생성되었다면 true를 리턴하고 그렇지 않으면 false를 리턴한다.
boolean result = 좌항(객체) instanceof 우항(타입)
instanceof 연산자는 주로 매개값의 타입을 조사할 때 사용한다. 메소드 내에서 강제 타입 변환이 필요할 경우 반드시 매개값이 어떤 객체인지 instanceof 연산자로 확인하고 안전하게 강제 타입 변환을 해야 한다.
- 확인문제
1 - 3. 자동 타입 변환을 이용해서 필드와 매개 변수의 다형성을 구현한다. (o)
-> 메소드 재정의도 같이해야 다형성이 구현된다고 생각해서 x를 고름. 하지만 자동 타입 변환만으로도 필드와 매개변수의 다형성을 구현할 수 있다.
필드의 다형성 : 상위 클래스 타입의 참조 변수로 하위 클래스의 객체를 참조할 수 있다. 이는 메소드 재정의 없이도 가능.
매개변수의 다형성 : 메소드의 매개변수로 상위 클래스 타입을 사용하면, 해당 메소드에 하위 클래스의 객체를 전달할 수 있다. 이 역시 메소드 재정의 없이 가능하다.
메소드 재정의는 다형성의 또 다른 중요한 측면인 '메소드 다형성'을 구현하는데 필요하다. 하지만 필드와 매개변수의 다형성만을 고려한다면, 자동 타입 변환만으로 충분히 구현 가능하다.
2.
// Tire.java
package sec02.verify.exam02;
public class Tire {
public void run() {
System.out.println("일반 타이어가 굴러갑니다.");
}
}
// SnowTire.java
package sec02.verify.exam02;
public class SnowTire extends Tire {
@Override
public void run() {
System.out.println("스노우 타이어가 굴러갑니다.");
}
}
// SnowTireExample.java
package sec02.verify.exam02;
public class SnowTireExample {
public static void main(String[] args) {
SnowTire snowTire = new SnowTire();
Tire tire = snowTire;
snowTire.run();
tire.run();
}
}
스노우 타이어가 굴러갑니다.
스노우 타이어가 굴러갑니다.
-> Tire tire = snowTire; 이 부분에서 자동 타입 변환됨. 하지만 이는 단지 참조 변수의 타입만 변환되는 것이지 실제 객체가 변하는 것은 아니다. 실제 객체의 오바라이딩(메소드 재정의)된 메소드가 호출된다. 부모 클래스 타입의 변수로 자식 클래스의 객체를 참조하더라도 오버라이딩된 메소드가 호출될 때는 실제 객체의 메소드가 실행된다.
(1), (2)는 인터페이스로, 직접 인스턴스화할 수 없다.
(5)번은 상속이 이어져 있지 않아 할 수 없다.
(6)번은 MemberService와 직접적인 관계가 없어 사용할 수 없다.
7 - 3 추상 클래스
객체를 직접 생성할 수 있는 클래스를 실체 클래스라고 한다면 이 클래스들의 공통적인 특성을 추출해 선언한 클래스를 추상클래스라고 한다. 추상 클래스와 실체 클래스는 상속의 관계를 가지고 있다. 추상클래스가 부모, 실체클래스가 자식으로 구현되어 실체 클래스는 추상 클래스의 모든 특성을 물려받고, 추가적인 특성을 가질 수 있다. 특성은 필드와 메소드를 말한다.
- 추상클래스 선언
클래스 선언에 abstract 키워드를 붙여야 한다. abstract를 붙이면 new 연산자를 이용해서 객체를 만들지 못하고, 상속을 통해 자식 클래스만 만들 수 있다. 추상 클래스도 일반 클래스와 마찬가지로 필드, 생성자, 메소드 선언할 수 있다. new 연산자로 직접 생성자를 호출할 수는 없지만 자식 객체가 생성될 때 super(...)를 호출해서 추상클래스 객체를 생성하므로 추상클래스도 생성자가 반드시 있어야 한다.
public abstract class 클래스 {
//필드
//생성자
//메소드
}
추상 클래스는 실체 클래스의 공통되는 필드와 메소드를 추출해서 만들었기 때문에 객체를 직접 생성해서 사용할 수 없다. 추상 클래스는 새로운 실체 클래스를 만들기 위해 부모 클래스로만 사용된다. 코드로 설명하면 extends 뒤에만 올 수 있는 클래스이다.
- 추상메소드와 재정의
추상클래스는 실체클래스가 공통적으로 가져야 할 필드와 메소드들을 정의해 놓은 추상적인 클래스로 실체클래스의 멤버(필드, 메소드)를 통일하는데 목적. 모든 실체들이 가지고 있는 메소드의 실행 내용이 동일하다면 추상 클래스에 메소드를 작성하는 것이 좋다. 하지만 메소드의 선언만 통일하고, 실행 내용은 실체 클래스마다 달라야 하는 경우가 있다.
ex) 모든 동물은 소리를 내지만, 동물마다 내는 소리가 다른 경우
이런 경우를 위해 추상클래스는 추상메소드를 선언할 수 있다. 추상메소드는 abstract 키워드와 함께 메소드의 선언부만 있고 메소드 실행 내용인 중괄호{}가 없는 메소드이다.
[public : protected] abstract 리턴타입 메소드이름(매개변수, ...);
추상클래스 설계 시 하위 클래스가 반드시 실행내용을 채우도록 강제하고 싶은 메소드가 있을 경우 해당 메소드를 추상 메소드를 선언. 자식 클래스는 반드시 추상 메소드를 재정의해서 실행내용을 작성해야 하는데, 그렇지 않으면 컴파일 에러 발생.
- 기본 숙제(클래스의 타입 변환에는 어떤 것이 있는지 정리하기 공유하기)
기본타입과 같이 클래스 타입에도 타입변환이 있다. 타입 변환에는 자동 타입변환과 강제 타입 변환이 있는데, 자동 타입 변환은 자식 객체를 부모 타입으로 대입할 때 자동 변환되고, 변환된 부모 타입 변수를 자식 타입에 대입하려는데 안될 때, 즉 원래 부모 타입을 자식 타입으로 바꾸고 싶을 때는 강제 타입 변환한다.
- 추가 숙제(p.389 (7-3) 확인 문제 3번을 풀고 풀이과정 설명하기)
package sec03.verify.exam03;
public class LoginServlet extends HttpServlet {
@Override
public void service() {
System.out.println("로그인 합니다.");
}
}
package sec03.verify.exam03;
public class FileDownloadServlet extends HttpServlet {
@Override
public void service() {
System.out.println("파일 다운로드 합니다.");
}
}
추상메소드 재정의하기
- 4주 차 회고
책 내용이 점층적으로 쌓이는 느낌으로 진행되는 느낌이다. 맛보기로 살짝 보여주고 뒤에서 그 이유를 설명해 주는데 이래서 이랬던 거구나 싶고 반갑다. 하지만 다형성이 이해가 가는듯하면서도 어렵다. 그리고 족장님이 추천해 주신 클로드 잘 쓰고 있는데 다른 글에도 추가할 거지만 클로드에게 틀린 이유로 생각한 것이 맞는지 확인하는 용도로 사용하는데 너무 좋다 친절하고 굿굿. 근데 프로로 구독해야 하는지 고민된다. 질문 잘하고 있으면 끝나는 게 아쉬워서,, 족장님은 구독하시는지 궁금합니당. 아 그리고 5주 차는 방학이지만 블로그에는 추가하지 못했던 내용 마무리하겠습니다...!
'혼공학습단12기' 카테고리의 다른 글
[혼공자]6주차 - chap10 (0) | 2024.08.15 |
---|---|
[혼공자]5주차 - chap08 (0) | 2024.08.05 |
[혼공자]3주차 - chap06 (0) | 2024.07.21 |
[혼공자]2주차 - chap04 (0) | 2024.07.13 |
[혼공자]1주차 - chap01 ~ 03 (0) | 2024.07.07 |