자바에서 오버라이드가 안되는 메서드는?

jonghyun.log·2023년 5월 1일
1

이전 글에서 자바의 오버라이드(Override)의 원리에 대해 다뤄봤는데 이번에는 조금 더 나아가서
오버라이드는 어떤 종류의 메서드에서 가능하고 안되는 메서드는 어떤것이 있는지 논해보겠습니다.

오버라이드가 안되는 메서드는?

우선 정답을 먼저 적어보자면

1. private 메서드

2. static 메서드

3. final이 붙은 메서드

이렇게 세가지 종류의 메서드는 오버라이드가 불가능합니다.

위의 메서드들이 오버라이드가 안되는 이유

자 그렇다면 위의 메서드들에서는 어째서 오버라이드가 불가능한 것 일까요?

우선 이전글에서 사용한 예제를 가져와서 이 주제를 논해보겠습니다.

class SuperClass {
    int x;

    public void f() {
        System.out.println("SuperClass");
    }
}


class SubClass extends SuperClass {

    @Override
    public void f() {
        System.out.println("SubClass");
    }

}
public class Test {

    public static void main(String[] args) {
    
		SuperClass superClass = new SubClass();
        superClass.f();
    }
}

출력결과
SubClass

오버라이드의 원리에 의해 하위타입의 인스턴스를 생성할 때 상위타입의 필드 및 메서드를 정의하기 위한 공간과 하위타입의 필드 및 메서드를 정의하기 위한 공간이 할당되고 JVM동적 바인딩에 의하여 하위타입의 f()메서드가 호출되어 SubClass가 출력되게 됩니다.

private 메서드

그렇다면 만약에 정의되어있는 메서드가 private 메서드인 경우에는 무슨일이 일어나게 될까요??

참고) 상위 하위 타입의 접근지정자가 다르면 다른 메서드로 인식하여 오버라이드가 불가능합니다.
즉, 오버라이드를 하기 위해서는 우선 메서드의 리턴타입 뿐만 아니라 접근지정자도 일치시켜 주는것이 전제입니다.

class SuperClass {

    private void g() {
        System.out.println("SuperClass");
    }
    
 }
 
 class SubClass extends SuperClass {

    @Override
    private void g() {
        System.out.println("SuperClass");
    }
    
}

위와 같은 코드를 작성하게 되면

다음과 같이 오버라이드 할 수 없다는 컴파일 에러를 띄워주게 됩니다.

그렇다면 왜 이런 현상이 발생하게 되는걸까요??

메서드 오버라이드의 원리는

  1. 컴파일 시점컴파일러정적 바인딩을 통해 상위 타입의 메서드로 인식을 합니다.
  2. 그 이후 프로그램 런타임 시점JVM이 인스턴스를 생성하게 되고.
  3. 그 인스턴스 안에 하위 타입의 메서드가 재정의 되어있으면 기존 정적 바인딩된 메서드 호출문을 대신 하위 타입의 메서드를 호출하는 것으로 대체하는 식으로 실행이 됩니다.

그리고 이 메서드 호출을 대체하는 과정을 동적 바인딩이라고 하였구요.

하지만, 지금 정의한 메서드가 private이기 떄문에 이야기가 조금 달리집니다.

잠시 private 접근 지정자에 대해 간단하게 짚어보면

private은 인스턴스에 접근하는 변수로 무조건 인스턴스 타입의 형과 동일한 형으로만 접근이 가능합니다.

그러면 다시 오버라이드된 메서드의 호출 과정을 다시 보게 되면

상위 타입의 참조변수로 메서드를 호출하면 SubClass형의 인스턴스 공간안의 SuperClass 형의 g()로 접근이 가능합니다. 하지만 동적바인딩에 의해 하위타입에 정의되어있는 g()를 호출하기 때문에 하위타입에 있는 private 메서드 g() 에는 접근이 불가능한 것이죠.

그렇다면 이번에는 하위 타입의 참조변수로 SubClass 인스턴스를 참조하는 경우에는 어떻게 될까요?

SubClass 타입의 인스턴스안의 SuperClass의 공간에 접근 해서 메서드들을 찾아보는데 g()private이기 때문에 참조변수가g()에 접근할 수가 없습니다.

따라서 private 메서드는 오버라이드가 불가능합니다.

static 메서드

이번에는 static 메서드를 한번 살펴봅시다.

스태틱 메서드는 객체 인스턴스에 종속되는 개념이 아닌 클래스에 종속 되는 개념입니다.

맨 처음에 메모리 공간에 딱 한개만 생성되며 호출시에도 JVM이 객체를 찾아서 호출하는 것이 아니라 컴파일 시점에 선언된 타입의 메서드를 호출하게 됩니다.

즉, 위에서 설명한 동적 바인딩을 구조적으로 해줄 수가 없는 것이죠.

이에 대해 잘 설명되어 있는 블로그 링크를 추가 설명으로 남깁니다.
출처 [JAVA] 정적 메소드를 오버라이드 하지 못하는 이유

final 메서드

예약어 final은 특정 변수나 메서드 혹은 클래스를 변화하지 못하게, 즉 상수로 만들어주는 역할도 하지만
final 자체가 클래스간 상속 금지메소드 오버라이딩의 금지를 담당하는 키워드 이기 때문에 불가능합니다.

클래스의 메서드 중 일부 또는 전부를 final 로 선언할 수 있습니다 .
메서드 선언에서 키워드를 사용하여 final메서드를 하위 클래스로 재정의할 수 없음을 나타냅니다. 클래스 Object는 이 작업을 수행합니다.
여러 메서드는 변경해서는 안 되는 구현이 있고 개체의 일관된 상태에 중요한 경우 메서드를 최종으로 만들고 싶을 수 있습니다.
출처 오라클 자바 api 공식문서 final 키워드

배운점

그동안 자바를 사용할 때 상속을 많이 사용을 하긴 했지만, 정작 이것의 매커니즘에는 딱히 고민해본적이 없던거 같습니다.

특히, 동적, 정적의 바인딩 개념과 힙 공간안에 인스턴스가 생성됐을때 그 인스턴스안에 상위, 하위 타입을 위한 공간이 생성된다는 것이 좀 충격이었네요.

한 인스턴스 안에 상위 하위 타입의 공간이 각각 생성되어서 위와 같은 것도 가능하다는,,

0개의 댓글