Computer >> 컴퓨터 >  >> 프로그래밍 >> Java

자바(Java)에서 메서드 오버라이딩을 방지하는 3가지 방법

자바에서 메서드 오버라이딩(Method Overriding)이 가능한 이유는 런타임에 이루어지는 동적 메서드 바인딩(Dynamic Binding) 덕분입니다. 반대로 특정 메서드에 대해 컴파일러가 정적 바인딩(Static Binding)을 수행하도록 만들면, 하위 클래스에서 해당 메서드를 재정의하는 것을 막을 수 있습니다.

자바에서 메서드 오버라이딩을 방지하는 대표적인 방법은 다음 세 가지입니다.

  • 기반 클래스(Base Class)에서 메서드를 final로 선언하기
  • 기반 클래스에서 메서드를 static으로 선언하기
  • 기반 클래스에서 메서드를 private으로 선언하기

1. final 메서드는 재정의할 수 없습니다

메서드를 final로 선언하면 하위 클래스에서 해당 메서드를 재정의할 수 없다는 제약이 추가됩니다. 상속 구조에서 특정 메서드의 동작이 변경되지 않도록 고정해야 할 때 가장 명확하게 사용할 수 있는 방법입니다.

예제 코드

class Base {
    public void show() {
        System.out.println("Base class show() method");
    }

    public final void test() {
        System.out.println("Base class test() method");
    }
}

class Derived extends Base {
    public void show() {
        System.out.println("Derived class show() method");
    }

    // Base 클래스의 test() 메서드가 final이므로 재정의할 수 없습니다
    /*
    public void test() {
        System.out.println("Derived class test() method");
    }
    */
}

public class Test {
    public static void main(String[] args) {
        Base ref = new Derived();

        // final 메서드 test() 호출
        ref.test();

        // 오버라이딩된 메서드 show() 호출
        ref.show();
    }
}

실행 결과

Base class test() method
Derived class show() method

2. static 메서드는 재정의할 수 없습니다

static 메서드는 객체가 아닌 클래스 자체에 속하기 때문에 재정의가 불가능합니다. 일반 인스턴스 메서드를 호출할 때 JVM은 this 참조를 전달하지만, static 메서드 호출 시에는 this 참조가 전달되지 않습니다. 따라서 static 메서드에는 런타임 바인딩이 적용될 수 없습니다.

하위 클래스에서 같은 시그니처의 static 메서드를 선언하더라도 이는 오버라이딩이 아니라 새로운 메서드를 정의하는 것(메서드 하이딩)으로 처리됩니다.

예제 코드

class Base {
    public void show() {
        System.out.println("Base class show() method");
    }

    public static void test() {
        System.out.println("Base class test() method");
    }
}

class Derived extends Base {
    public void show() {
        System.out.println("Derived class show() method");
    }

    // 이것은 오버라이딩이 아니라 Derived 클래스의 새로운 메서드로 취급됩니다
    public static void test() {
        System.out.println("Derived class test() method");
    }
}

public class Test {
    public static void main(String[] args) {
        Base ref = new Derived();

        // 정적 바인딩되므로 Base 클래스의 test()가 호출됩니다
        ref.test();

        // 오버라이딩된 메서드 show() 호출
        ref.show();
    }
}

실행 결과

Base class test() method
Derived class show() method

3. private 메서드는 재정의할 수 없습니다

private 메서드는 기반 클래스 내부에서만 접근할 수 있으므로 하위 클래스에서 보이지 않습니다. 따라서 하위 클래스에서 재정의하는 것 자체가 불가능합니다. 하위 클래스에 같은 이름의 메서드를 정의하더라도 이는 오버라이딩이 아니라 완전히 별개의 새로운 메서드로 취급됩니다.

예제 코드

class Base {
    public void show() {
        System.out.println("Base class show() method");
    }

    private void test() {
        System.out.println("Base class test() method");
    }
}

class Derived extends Base {
    public void show() {
        System.out.println("Derived class show() method");
    }

    // 이것은 오버라이딩이 아니라 완전히 별개의 새로운 메서드입니다
    public void test() {
        System.out.println("Derived class test() method");
    }
}

public class Test {
    public static void main(String[] args) {
        Base ref = new Derived();

        // private 메서드는 외부에서 호출할 수 없으며, 이 줄은 컴파일 오류를 발생시킵니다
        // ref.test();

        // 오버라이딩된 메서드 show() 호출
        ref.show();
    }
}

실행 결과

Derived class show() method

세 가지 방법 비교

방법동작 방식
final컴파일러가 재정의를 금지하며, 하위 클래스에서 재정의하려 하면 컴파일 오류가 발생합니다.
static런타임 바인딩이 적용되지 않아 재정의가 불가능하며, 같은 시그니처의 메서드는 메서드 하이딩으로 처리됩니다.
private하위 클래스에서 식별할 수 없어 재정의가 불가능하며, 같은 이름의 메서드는 완전히 새로운 메서드로 취급됩니다.

마무리

의도적으로 메서드의 재정의를 막고 싶다면 final 키워드를 사용하는 것이 가장 명확하고 권장되는 방법입니다. static과 private은 언어의 바인딩 규칙과 접근 제어 규칙에 따라 자연스럽게 재정의가 차단되는 경우에 해당한다는 점을 기억하시기 바랍니다.