programing

자바에서 개인 메서드 재정의

nasanasas 2020. 12. 15. 19:23
반응형

자바에서 개인 메서드 재정의


여기 에서 간결하게 설명했듯이 Java에서 private 메서드를 재정의하는 것은 부모 클래스의 개인 메서드가 "자동으로 최종적이고 파생 클래스에서 숨겨지기"때문에 유효하지 않습니다. 제 질문은 주로 학문적입니다.

어떻게는 하지 하지에 캡슐화 위반 (즉, 자식 클래스에서 동일한 서명으로, 독립적으로 구현) 부모의 개인 방법은 "무시"할 수 있도록? 캡슐화 원칙에 따라 부모의 private 메서드는 자식 클래스에서 액세스하거나 상속 할 수 없습니다. 숨겨져 있습니다.

그렇다면 왜 자식 클래스가 동일한 이름 / 서명으로 자체 메서드를 구현하지 못하도록 제한해야합니까? 이것에 대한 좋은 이론적 기초가 있습니까, 아니면 일종의 실용적인 해결책입니까? 다른 언어 (C ++ 또는 C #)에 대해 다른 규칙이 있습니까?


당신은 할 수 오버라이드 (override) 개인 방법을,하지만 당신은 문제없이 파생 클래스에서 하나를 소개 할 수 있습니다. 이것은 잘 컴파일됩니다.

class Base
{
   private void foo()
   {
   }
}

class Child extends Base
{
    private void foo()
    {
    }
}

@Override주석 을 적용하려고 Child.foo()하면 컴파일 타임 오류가 발생합니다. 그래서 당신이 당신이하는 경우 당신에게 경고 또는 오류를주는 컴파일러 / IDE 세트 가지고 없는@Override 주석을 모두 잘해야한다. 나는 C # 방식의 override키워드를 선호 하지만 자바로하기에는 너무 늦었다.

C #에서 private 메서드를 "재정의"하는 경우-private 메서드는 처음에는 가상이 될 수 없지만 기본 클래스의 private 메서드와 이름이 같은 새 private 메서드를 확실히 도입 할 수 있습니다.


글쎄, 개인 메서드를 덮어 쓰도록 허용하면 캡슐화 누출이나 보안 위험이 발생합니다. 가능 하다고 가정 하면 다음과 같은 상황이 발생합니다.

  1. private 메서드가 boolean hasCredentials()있고 확장 클래스가 다음과 같이 간단히 재정의 할 수 있다고 가정 해 보겠습니다 .

    boolean hasCredentials() { return true; }
    

    따라서 보안 검사를 위반합니다.

  2. 이를 방지하는 원래 클래스의 유일한 방법은 메서드를 선언하는 것 final입니다. 그러나 이제는 파생 클래스가 더 이상 메서드를 만들 없기 때문에 캡슐화를 통해 구현 정보가 누출 hasCredentials됩니다. 기본 클래스에 정의 된 메서드 와 충돌합니다.

    그것은 나쁘다 :이 방법이 처음에 존재하지 않는다고하자 Base. 이제 구현자는 합법적으로 클래스를 파생시키고 예상대로 작동 Derived하는 메서드 hasCredentials제공 할 수 있습니다 .

    그러나 이제 원래 클래스 버전 Base이 출시되었습니다. 퍼블릭 인터페이스는 변경되지 않으며 (불변하지도 않음) 기존 코드를 손상시키지 않을 것으로 예상해야합니다. 이제 파생 클래스의 메서드와 이름 충돌이 있기 때문에 그렇게합니다.

질문은 오해에서 비롯된 것 같습니다.

부모의 private 메서드가 "무시"되는 것을 허용하지 않는 것이 캡슐화 위반이 아닌가? (즉, 자식 클래스에서 동일한 서명을 사용하여 독립적으로 구현 됨)

괄호 안의 텍스트는 앞의 텍스트 와 반대 입니다. 자바는 않습니다 당신이 "독립적으로 자식 클래스에서 동일한 서명, [개인 방법]을 구현"할 수 있습니다. 이것을 허용하지 않으면 위에서 설명한 것처럼 캡슐화에 위배됩니다.

그러나 "부모의 개인 메서드가"무시 "되는 것을 허용하지 않는 것"은 캡슐화 보장 하는 데 필요한 다른 것 입니다.


"다른 언어 (C ++ 또는 C #)에 대해 다른 규칙이 있습니까?"

음, C ++에는 다른 규칙이 있습니다. 정적 또는 동적 멤버 함수 바인딩 프로세스와 액세스 권한 적용은 직교합니다.

멤버 함수에 private액세스 권한 수정자를 제공한다는 것은이 함수가 선언 클래스에 의해서만 호출 될 수 있고 다른 클래스 (파생 클래스도 아님)에 의해서만 호출 될 수 있음을 의미합니다. private멤버 함수를 virtual순수 가상 ( virtual void foo() = 0;) 으로 선언 하면 액세스 권한을 계속 적용하면서 기본 클래스가 특수화의 혜택을받을 수 있습니다.

그것이 올 때 virtual멤버 함수, 액세스 권한 당신이 어떻게해야하는지 알려줍니다 :

  • private virtual 동작을 전문화 할 수 있지만 멤버 함수의 호출은 확실히 제어 된 방식으로 기본 클래스에 의해 수행됨을 의미합니다.
  • protected virtual 이는 멤버 함수를 재정의 할 때 상위 클래스 버전의 멤버 함수를 호출해야 함을 의미합니다.

따라서 C ++에서 액세스 권한과 가상 성은 서로 독립적입니다. 함수가 정적으로 또는 동적으로 바인딩되는지 여부를 결정하는 것은 함수 호출을 해결하는 마지막 단계입니다.

마지막으로 템플릿 메소드 디자인 패턴이 public virtual멤버 함수 보다 선호되어야합니다 .

참조 : 대화 : 사실상 귀하의 것

이 기사는 private virtual멤버 함수 의 실제 사용을 제공 합니다.


ISO / IEC 14882-2003 §3.4.1

이름 조회는 이름이 함수 이름 인 것을 발견하면 하나 이상의 선언을 이름과 연관시킬 수 있습니다. 선언은 오버로드 된 함수 집합을 형성한다고합니다 (13.1). 오버로드 확인 (13.3)은 이름 조회가 성공한 후에 발생합니다. 액세스 규칙 (11 절)은 이름 조회 및 기능 과부하 해결 (해당되는 경우)이 성공한 경우에만 고려됩니다. 이름 조회, 함수 오버로드 확인 (해당되는 경우) 및 액세스 검사가 성공한 후에 만 ​​식 처리 (5 절)에서 추가로 사용되는 이름 선언에 의해 도입 된 속성이 있습니다.

ISO / IEC 14882-2003 §5.2.2

멤버 함수 호출에서 호출되는 함수는 일반적으로 객체 표현식의 정적 유형 (절 10)에 따라 선택되지만, 해당 함수가 aqualified-id를 사용하여 지정되지 않은 경우 실제로 호출 된 함수는 다음의 최종 오버라이드 (10.3)가됩니다. 객체 표현식의 동적 유형에서 선택된 함수 [참고 : 동적 유형은 객체 표현식의 현재 값이 가리 키거나 참조하는 객체의 유형입니다.


부모의 private 메서드는 캡슐화 원칙에 따라 자식 클래스가 액세스하거나 상속 할 수 없습니다. 숨겨져 있습니다.

그렇다면 왜 자식 클래스가 동일한 이름 / 서명으로 자체 메서드를 구현하지 못하도록 제한해야합니까?

그러한 제한은 없습니다. 아무 문제없이 그렇게 할 수 있습니다. "무시"라고하는 것이 아닙니다.

재정의 된 메서드는 동적 디스패치의 영향을받습니다. 즉, 실제로 호출되는 메서드는 호출되는 개체의 실제 유형에 따라 런타임에 선택됩니다. 개인 방법을 사용하면 발생하지 않습니다 (첫 번째 진술에 따라 안됨). 그리고 이것이 "개인 메서드는 재정의 할 수 없습니다"라는 문이 의미하는 것입니다.


나는 당신이 그 게시물이 말하는 것을 오해하고 있다고 생각합니다. 그건 하지 하위 클래스 "같은 이름 / 서명을 자신의 방법을 구현하는 제한."것을 말

다음은 약간 편집 된 코드입니다.

public class PrivateOverride {
  private static Test monitor = new Test();

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

  public static void main(String[] args) {
    PrivateOverride po = new Derived();
    po.f();
    });
  }
}

class Derived extends PrivateOverride {
  public void f() {
    System.out.println("public f()");
  }
}

그리고 인용문 :

출력이“public f ()”일 것이라고 합리적으로 예상 할 수 있습니다.

The reason for that quote is that the variable po actually holds an instance of Derived. However, since the method is defined as private, the compiler actually looks at the type of the variable, rather than the type of the object. And it translates the method call into invokespecial (I think that's the right opcode, haven't checked JVM spec) rather than invokeinstance.


It seems to be a matter of choice and definition. The reason you can't do this in java is because the specification says so, but the question were more why the specification says so.

The fact that C++ allows this (even if we use virtual keyword to force dynamic dispatch) shows that there is no inherent reason why you couldn't allow this.

However it seem to be perfectly legal to replace the method:

class B {
    private int foo() 
    {
        return 42;
    }

    public int bar()
    {
        return foo();
    }
}

class D extends B {
    private int foo()
    {
        return 43;
    }

    public int frob()
    {
        return foo();
    }
}

Seems to compile OK (on my compiler), but the D.foo is not related to B.foo (ie it doesn't override it) - bar() always return 42 (by calling B.foo) and frob() always returns 43 (by calling D.foo) no matter whether called on a B or D instance.

One reason that Java does not allow override the method would be that they didn't like to allow the method to be changed as in Konrad Rudolph's example. Note that C++ differs here as you need to use the "virtual" keyword in order to get dynamic dispatch - by default it hasn't so you can't modify code in base class that relies on the hasCredentials method. The above example also protects against this as the D.foo does not replace calls to foo from B.


When the method is private, it's not visible to its child. So there is no meaning of overriding it.


I apologize for using the term override incorrectly and inconsistent with my description. My description describes the scenario. The following code extends Jon Skeet's example to portray my scenario:

class Base {
   public void callFoo() {
     foo();
   }
   private void foo() {
   }
}

class Child extends Base {
    private void foo() {
    }
}

Usage is like the following:

Child c = new Child();
c.callFoo();

The issue I experienced is that the parent foo() method was being called even though, as the code shows, I was calling callFoo() on the child instance variable. I thought I was defining a new private method foo() in Child() which the inherited callFoo() method would call, but I think some of what kdgregory has said may apply to my scenario - possibly due to the way the derived class constructor is calling super(), or perhaps not.

There was no compiler warning in Eclipse and the code did compile. The result was unexpected.


Beyond anything said before, there's a very semantic reason for not allowing private methods to be overridden...THEY'RE PRIVATE!!!

If I write a class, and I indicate that a method is 'private', it should be completely unseeable by the outside world. Nobody should be able access it, override it, or anything else. I simply ought to be able to know that it is MY method exclusively and that nobody else is going to muck with it or depend on it. It could not be considered private if someone could muck with it. I believe that it's that simple really.


A class is defined by what methods it makes available and how they behave. Not how those are implemented internally (e.g. via calls to private methods).

Because encapsulation has to do with behavior and not implementation details, private methods have nothing to do with the idea encapsulation. In a sense, your question makes no sense. It's like asking "How is putting cream in coffee not a violation of encapsulation?"

Presumably the private method is used by something that is public. You can override that. In doing so, you've changed behavior.

ReferenceURL : https://stackoverflow.com/questions/2000137/overriding-private-methods-in-java

반응형