클래스를 수평적 구조로 설계할 시 메서드 측면에서도 중복이 발생할 수 있다.
public class Dog {
public void eat() {
System.out,println("개처럼 먹다");
}
}
public class Cat {
public void eat() {
System.out,println("고양이처럼 먹다");
}
public void eyeLight() {
System.out.println("밤에 눈에서 빛이 난다");
}
}
Dog class의 eat()메서드와 Cat class의 eat()메서드에서 중복이 발생한다.
public class AnimalTest {
public static void main(String[] args) {
Dog d = new Dog();
d.eat();
Cat c = new Cat();
c.eat();
c.night();
}
}
중복을 해결하기 위해 상속을 활용해 수직적 구조로 설계해보자
public class Animal {
public void eat() {
System.out,println("먹다");
}
}
public class Dog extends Animal {
}
----------------------------------
public class Cat {
public void eyeLight() {
System.out.println("밤에 눈에서 빛이 난다");
}
}
public class AnimalTest {
public static void main(String[] args) {
Dog d = new Dog();
d.eat();
Cat c = new Cat();
c.eat();
c.night();
}
}
상속을 통해 메서드 중복은 해결되었지만, 코드의 추상성이 증대되었다.
상속관계에서 객체생성 시 부모가 자식을 가리키는 upcasting방식을 사용해 객체를 생성하는 것이 바람직하다.
실행 시점에서 사용될(호출될)메서드가 결정(연결)되는 바인딩
정적 바인딩의 반대 개념으로 컴파일 시점에 호출될 메서드가 결정되는 게 아닌 메서드 실행 시점에 사용될 메서드가 결정된다. 메서드 오버라이딩의 경우, 실행 시점에 오버라이딩한 메서드를 찾아서 실행을 결정한다.
메서드의 재정의(Override)는 왜 필요할까? 상속관계에서 자식클래스이 부모클래스의 메서드를 상속받아 자유롭게 사용할 수 있지만, 그대로 상속받아 사용하기에는 자식클래스에게 맞지 않을 수 있다. 이 때 상속받은 메서드를 자식클래스에 맞게 재정의(Override)하는 것이다.
오버라이딩을 통해 업캐스팅을 통해 객체생성을 한 경우에도 자식클래스의 메서드에 접근할 수 있게 된다.
public class Dog extends Animal {
@Override
public void eat() { // 메서드 오버라이드
System.out.println(super.getClass().getSimpleName() + " : " + "개처럼 먹다");
}
}
------------------------------------------
public class Cat extends Animal {
@Override
public void eat() { // 메서드 오버라이드
System.out.println(super.getClass().getSimpleName() + " : " + "고양이처럼 먹다");
}
}
public class AnimalTest {
public static void main(String[] args) {
Animal animal1 = new Dog(); // upcasting
Animal animal2 = new Cat();
animal1.eat(); // 동물처럼먹다 -> 개처럼먹다 (오버라이드됨) : 실행시점에 실행 메서드 결정
animal2.eat();
}
}
<esult>
Dog : 개처럼 먹다
Cat : 고양이처럼 먹다