IS-A 관계를 통해 생성된 클래스 및 객체는 상속 관계에서 둘은 밀접하게 결합되므로 부모 또는 기저 클래스의 명세에 변경이 발생하면 코드가 손상될 위험이 있습니다. 대신에 이러한 밀접한 관계는 클래스 계층구조에서 좀 더 안정적인 기반을 마련한다는 의미이기도 합니다. 게다가 상위 클래스의 기능을 하위 클래스가 물려받아 사용할 수 있는 장점도 있습니다.
반면 HAS-A 방식으로 생성된 클래스 및 객체는 느슨하게 결합됩니다. 이는 상속에 비해서 명세에 변경이 발생하더라도 구성 요소를 쉽게 변경할 수 있다는 의미입니다(코드의 손상이 적거나 없다는 의미입니다). 이러한 점에서 더 많은 유연성을 제공합니다. 하지만, HAS-A 방식은 상속보다 항상 낫다고 말할 정도로 단순한 문제가 아니며 실상은 더 복잡합니다.
객체의 합성/포함
class Car {
String name;
Accelerator accelerator;
Engine engine;
Handle handle;
Wheel wheel;
Car(String name, Accelerator accelerator, Engine engine, Handle handle, Wheel wheel) {
this.name = name;
this.accelerator = accelerator;
this.engine = engine;
this.handle = handle;
this.wheel = wheel;
}
}
Accelerator accelerator = new Accelerator("이쁜 자동차");
Engine engine = new Engine("이쁜 자동차");
Handle handle = new Handle();
Wheel wheel = new Wheel("빠른 바퀴");
Car car = new Car("이쁜 자동차", accelerator, engine, handle, wheel);
다형성
class A {
public String x() {
return "A.x";
}
}
class B extends A {
public String y() {
return "y"
}
A obj = new B();
obj.x(); //A.x
obj.y(); //에러. obj의 데이터타입은 A이기 때문.
class A {
public String x() {
return "A.x";
}
}
class B extends A {
public String x() {
return "B.x"; //오버라이딩
public String y() {
return "y"
}
class B2 extends A {
public String x() {
return "B2.x";
}
A obj = new B();
A obj2 = new B2();
obj.x(); //B.x
왜 이렇게 하는가?
결과: 인스턴스가 A인것처럼 동작하게 할 수 있음.
public class Dog {
protected String color;
public void bite() {
System.out.println("깨물다");
}
public void bark() {
System.out.println("짖는다");
}
}
public class Bulldog extends Dog {
}
public class Retriever extends Dog {
public void swim() {
System.out.println("수영하다");
}
}

<--- 이 순서로 읽음.
Bulldog bulldog = new Bulldog(); //Bulldog is a Bulldog
Dog dog = new BullDog(); //Bulldog is a Dog
Retriever retriever = new Retriever(); //Retriever is a Retriever
Dog dog = new Retriever(); //Retriever is a Dog
Dog dog = new Retriever();
Retriever retriever = (Retriever)dog;
retriever.swim(); //가능
Dog dog = new Bulldog(); //Bulldog is a Dog
Bulldog bulldog = (Bulldog)dog;
-> Dog와 Bulldog의 구성은 같으므로 형변환 가능.
Dog dog = new Dog(); //Dog is a Dog
Retirever retriever = (Retriever)dog; //에러 발생, 개는 리트리버가 될 수 없다.
-> Dog는 .swim 메소드가 없으므로 형변환 불가.
package day08;
public class EX04 {
public static void main(String[] args) {
//다형성 안 쓸 경우
Chicken chicken = new Chicken();
chicken.cry();
Cow cow = new Cow();
cow.cry();
//다형성 쓸 경우
Farm farm = new Farm();
Animal chicken_ = new Chicken();
farm.sound(chicken_);
Animal cow_ = new Cow();
farm.sound(cow_);
//다형성을 쓰는 이유: 메소드에 업캐스팅된 부모타입의 변수를 넣어서, 자식 클래스 아무나 올 수 있게.
//만약 안쓰면 Farm안데 sound(Cow cow), sound(Chicken chicken) 처럼 동물 수만큼 메서드를 생성해서 계속 오버로딩해야함.
//Cow와 Chicken이 공통점은 Animal임.
}
}
class Farm {
public void sound(Animal animal) {
animal.cry();
}
}
class Animal {
public void cry() {
System.out.println("소리 없음");
}
}
class Chicken extends Animal {
@Override
public void cry() {
System.out.println("꼬꼬댁");
}
public void fly() {
System.out.println("파닥파닥");
}
}
class Cow extends Animal {
@Override
public void cry() {
System.out.println("음메");
}
public void eat() {
System.out.println("우물우물");
}
}