상속 구조에서 필드와 접근 메서드의 실질적 동작 분석
Java의 상속 시스템은 단순히 코드를 물려받는 것을 넘어서, 런타임 시점의 메서드 호출 방식에 따라 의도치 않은 동작이 발생할 수 있습니다. 아래 사례를 통해 이러한 현상을 명확히 이해해보겠습니다.
기본 예제: 필드 충돌과 메서드 오버라이딩
public class Freath {
private int a = 1;
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
}
public class A extends Freath {
private int a = 2;
@Override
public int getA() {
return a;
}
@Override
public void setA(int a) {
this.a = a;
}
}
public class B extends Freath {
private int a = 3;
@Override
public int getA() {
return a;
}
@Override
public void setA(int a) {
this.a = a;
}
}
public class Test {
public static void main(String[] args) {
Freath fa = new A();
Freath fb = new B();
System.out.println(fa.getA()); // 출력: 2
System.out.println(((A) fa).getA()); // 출력: 2
System.out.println(fb.getA()); // 출력: 3
System.out.println(((B) fb).getA()); // 출력: 3
}
}
이 경우 출력 결과가 자식 클래스의 값(2, 3)을 반환하는 이유는 다음과 같습니다:
Freath클래스의a필드는private로 선언되어 있으며, 자식 클래스에서는 직접 접근 불가입니다.- 그러나
getA()메서드는public으로 정의되어 있고, 자식 클래스에서 오버라이딩되었습니다. new A()로 인스턴스화하면, 실제 객체는A타입이며,getA()호출 시 런타임에 오버라이딩된 메서드가 실행됩니다.- 따라서
this.a는A클래스 내부의a값을 참조하게 되고, 이는2입니다.
즉, 메서드 오버라이딩은 객체의 실제 타입에 따라 결정되며, 필드는 각 클래스마다 독립적으로 존재합니다.
변경된 예제: 메서드 오버라이딩 미비 시 동작 차이
public class A extends Freath {
private int a = 2;
// getA(), setA() 메서드는 오버라이딩되지 않음
}
public class B extends Freath {
private int a = 3;
// getA(), setA() 메서드는 오버라이딩되지 않음
}
public class Test {
public static void main(String[] args) {
Freath fa = new A();
Freath fb = new B();
System.out.println(fa.getA()); // 출력: 1
System.out.println(((A) fa).getA()); // 출력: 1
System.out.println(fb.getA()); // 출력: 1
System.out.println(((B) fb).getA()); // 출력: 1
}
}
이번에는 모든 출력값이 1로 나오는 이유는:
- 자식 클래스
A,B가getA()메서드를 오버라이딩하지 않았으므로, 부모 클래스의getA()가 그대로 사용됩니다. getA()내부의this.a는 현재 객체의a필드를 참조합니다.- 하지만
A나B의a는private이므로,getA()메서드 내부에서는 부모 클래스의a필드를 참조하게 됩니다 (자식 클래스에서a필드는 가시성 제한으로 접근 불가). - 결국,
getA()는 부모 클래스의a값을 반환하며, 초기값1이 출력됩니다.
자식 클래스에서 상속된 필드 접근 제한과 메서드 오버라이딩의 관계
결론적으로, 자바의 상속에서 메서드 오버라이딩은 런타임 다형성 기반이며, 필드는 각 클래스마다 별도로 존재합니다. 따라서:
- 메서드 오버라이딩이 있을 경우 → 실제 객체 타입에 따른 값이 반환됨
- 메서드 오버라이딩이 없을 경우 → 부모 클래스의 필드 값이 반환됨
이러한 원리는 객체지향 설계에서 주의 깊게 고려해야 할 핵심 개념입니다.