이번 장에서의 상속은 클래스가 다른 클래스를 확장하는 구현 상속을 의미한다.
인터페이스가 다른 인터페이스를 확장하는 인터페이스 상속과는 무관하다.
느슨하게 결합된 코드는 더 많은 유연셩을 제공하기 때문에 상속보다는 컴포지션을 사용하는 것을 권장한다.
effective java에서도 상속보다는 컴포지션을 사용하기를 권장한다.(item 18)
하지만 권장이며 모든 프로그래밍 시나리오에 대해서 컴포지션을 사용하면 안된다.
상속은 extend를 활용하여 한 클래스를 다른 클래스에서 derive 즉 파생시킨다.
ex) animal이라는 클래스를 extend 받아 확장한 사람이라는 클래스가 파생된다.
public class human extends animal{
//... do something
}
컴포지션은 parts 즉 클래스를 구성하는 부분의 합으로 정의한다
ex) 클래스 필드 내에 private or public 필드로 클래스의 인스턴스를 참조하게 하고
해당 클래스를 구성하는 부분의 합으로 정의된다.
클래스의 구성요소로 쓰인다는 뜻에서 composition이라고 한다.
public class House {
private Bedroom bedroom;
private LivingRoom livingRoom;
//.. do something
}
상위 클래스와 하위 클래스가 “ is a ” 관계인 경우. “is a kind of” 가 좀 더 명확하다
• A cat is an animal , A cat is a kind of an animal
• A car is a vehicle, A car is a kind of a vehicle
해당 관계에서의 서브 클래스, 하위 클래스는 상위 클래스의 specialized version이다
즉 원본 버전과 동일하나 좀 더 구체화한, 좀 더 확장한, 좀 더 특별한 버전
슈퍼 클래스, 상위 클래스를 상속하는 것은 코드 재사용의 예시라고 볼 수 있다.
상속관계를 구성할 때는 subclass 가 superclass의 specialized version 인지 아닌지 체크하는 게 중요하다.
간단한 예시로 Vehicle과 Car의 예시를 보도록 하자.
class Vehicle {
String brand;
String color;
double weight;
double speed;
void move() {
System.out.println("The vehicle is moving");
}
}
public class Car extends Vehicle {
String licensePlateNumber;
String owner;
String bodyStyle;
public static void main(String... inheritanceExample) {
System.out.println(new Vehicle().brand);
System.out.println(new Car().brand);
new Car().move();
}
}
one object가 또 다른 object를 “has” or “is part of” 하는 경우 컴포지션을 사용할 수 있다.
• A car has a battery (a battery is part of a car).
• A person has a heart (a heart is part of a person).
public class CompositionExample {
public static void main(String... houseComposition) {
new House(new Bedroom(), new LivingRoom());
// The house now is composed with a Bedroom and a LivingRoom
}
static class House {
Bedroom bedroom;
LivingRoom livingRoom;
House(Bedroom bedroom, LivingRoom livingRoom) {
this.bedroom = bedroom;
this.livingRoom = livingRoom;
}
}
static class Bedroom { }
static class LivingRoom { }
}
🤔 상속에 대한 예시가 맞을까?
import java.util.HashSet;
public class CharacterBadExampleInheritance extends HashSet<Object> {
public static void main(String... badExampleOfInheritance) {
BadExampleInheritance badExampleInheritance = new BadExampleInheritance();
badExampleInheritance.add("Homer");
badExampleInheritance.forEach(System.out::println);
}
매우 적합하지 않다
🤔 컴포지션으로 바꾼다면?
import java.util.HashSet;
import java.util.Set;
public class CharacterCompositionExample {
static Set<String> set = new HashSet<>();
public static void main(String... goodExampleOfComposition) {
set.add("Homer");
set.forEach(System.out::println);
}
예시
class IndexOutOfBoundsException extends RuntimeException {...}
class ArrayIndexOutOfBoundsException extends IndexOutOfBoundsException {...}
class FileWriter extends OutputStreamWriter {...}
class OutputStreamWriter extends Writer {...}
interface Stream<T> extends BaseStream<T, Stream<T>> {...}
상속은 강력하지만 취약점이 존재한다.
상황에 맞춰서 상속 또는 컴포지션을 잘 활용하자.
https://www.infoworld.com/article/3409071/java-challenger-7-debugging-java-inheritance.html
http://www.yes24.com/Product/Goods/65551284