자바 상속에서 필드와 메서드 오버라이딩의 동작 원리

상속 구조에서 필드와 접근 메서드의 실질적 동작 분석

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.aA 클래스 내부의 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, BgetA() 메서드를 오버라이딩하지 않았으므로, 부모 클래스의 getA()가 그대로 사용됩니다.
  • getA() 내부의 this.a현재 객체의 a 필드를 참조합니다.
  • 하지만 ABaprivate이므로, getA() 메서드 내부에서는 부모 클래스의 a 필드를 참조하게 됩니다 (자식 클래스에서 a 필드는 가시성 제한으로 접근 불가).
  • 결국, getA()는 부모 클래스의 a 값을 반환하며, 초기값 1이 출력됩니다.

상속 시 필드 접근 범위 자식 클래스에서 상속된 필드 접근 제한과 메서드 오버라이딩의 관계

결론적으로, 자바의 상속에서 메서드 오버라이딩은 런타임 다형성 기반이며, 필드는 각 클래스마다 별도로 존재합니다. 따라서:

  • 메서드 오버라이딩이 있을 경우 → 실제 객체 타입에 따른 값이 반환됨
  • 메서드 오버라이딩이 없을 경우 → 부모 클래스의 필드 값이 반환됨

이러한 원리는 객체지향 설계에서 주의 깊게 고려해야 할 핵심 개념입니다.

태그: java 상속 메서드 오버라이딩 필드 접근 제한 다형성

5월 21일 06:09에 게시됨