자바 : 재정의 된 메서드를 호출하는 슈퍼 메서드 호출


94
public class SuperClass
{
    public void method1()
    {
        System.out.println("superclass method1");
        this.method2();
    }

    public void method2()
    {
        System.out.println("superclass method2");
    }

}

public class SubClass extends SuperClass
{
    @Override
    public void method1()
    {
        System.out.println("subclass method1");
        super.method1();
    }

    @Override
    public void method2()
    {
        System.out.println("subclass method2");
    }
}



public class Demo 
{
    public static void main(String[] args) 
    {
        SubClass mSubClass = new SubClass();
        mSubClass.method1();
    }
}

내 예상 출력 :

서브 클래스 method1
수퍼 클래스 method1
수퍼 클래스 method2

실제 출력 :

서브 클래스 method1
수퍼 클래스 method1
서브 클래스 method2

기술적으로는 내가 공개 메서드를 재정의 한 것을 알고 있지만, 수퍼를 호출했기 때문에 수퍼 내의 모든 호출이 수퍼에 남아있을 것이라고 생각했습니다. 어떻게 할 수 있는지에 대한 아이디어가 있습니까?


2
나는 당신이 "상속보다 구성을 선호"하고 싶을 것이라고 생각한다.
Tom Hawtin-tackline 2011 년

답변:


79

키워드 super는 "고정"되지 않습니다. 모든 메서드 호출은 개별적으로 처리되므로 SuperClass.method1()를 호출 super하여 수행하더라도 향후 수행 할 수있는 다른 메서드 호출에는 영향을 미치지 않습니다.

그 말은 호출하는 직접적인 방법이 없습니다 SuperClass.method2()에서 SuperClass.method1()비록 거치지 않고 SubClass.method2()당신의 실제 인스턴스와 작업을하지 않는 한 SuperClass.

Reflection을 사용하여 원하는 효과를 얻을 수도 없습니다 ( 문서java.lang.reflect.Method.invoke(Object, Object...) 참조 ).

[편집] 아직도 약간의 혼란이있는 것 같습니다. 다른 설명을 해보겠습니다.

호출 할 때 foo()실제로 호출 this.foo()합니다. Java는 단순히 this. 질문의 예에서 유형은 this입니다 SubClass.

따라서 Java가에서 코드를 실행하면 SuperClass.method1()결국this.method2();

를 사용 super해도에서 가리키는 인스턴스는 변경되지 않습니다 this. 그래서 호출로 이동 SubClass.method2()하기 때문에 this유형입니다 SubClass.

Java가 this숨겨진 첫 번째 매개 변수로 전달된다고 생각하면 이해하기 더 쉬울 것입니다 .

public class SuperClass
{
    public void method1(SuperClass this)
    {
        System.out.println("superclass method1");
        this.method2(this); // <--- this == mSubClass
    }

    public void method2(SuperClass this)
    {
        System.out.println("superclass method2");
    }

}

public class SubClass extends SuperClass
{
    @Override
    public void method1(SubClass this)
    {
        System.out.println("subclass method1");
        super.method1(this);
    }

    @Override
    public void method2(SubClass this)
    {
        System.out.println("subclass method2");
    }
}



public class Demo 
{
    public static void main(String[] args) 
    {
        SubClass mSubClass = new SubClass();
        mSubClass.method1(mSubClass);
    }
}

호출 스택을 따라 가면 this절대 변경되지 않으며 항상 .NET에서 생성 된 인스턴스 임을 알 수 있습니다 main().


누군가 스택을 통과하는이 (말장난 의도) 다이어그램을 업로드 할 수 있습니까? 미리 감사드립니다!
laycat

2
@laycat : 다이어그램이 필요하지 않습니다. Java에는 .NET 용 "메모리"가 없습니다 super. 메소드를 호출 할 때마다 인스턴스 유형을보고 .NET을 호출 한 빈도에 관계없이이 유형의 메소드 검색을 시작합니다 super. 따라서 method2의 인스턴스 를 호출 SubClass하면 항상 SubClass첫 번째 인스턴스가 표시됩니다 .
Aaron Digulla 2014

@AaronDigulla, "자바에는 수퍼 메모리가 없습니다"에 대해 더 설명해 주시겠습니까?
MengT 2014 년

@ Truman'sworld : 내 대답에서 말했듯이 : 사용 super은 인스턴스를 변경하지 않습니다. "지금부터는 모든 메서드 호출이 SuperClass"를 사용하여 시작해야합니다. "라는 일부 숨겨진 필드를 설정하지 않습니다 . 또는 다른 말로하면 :의 가치는 this변하지 않습니다.
아론 Digulla

@AaronDigulla, 슈퍼 키워드가 실제로 슈퍼 클래스로 이동하는 대신 하위 클래스에서 상속 된 메서드를 호출한다는 의미입니까?
MengT 2014 년

15

재정의 메서드 (또는 재정의 클래스의 다른 메서드)에서만 재정의 된 메서드에 액세스 할 수 있습니다.

따라서 재정의 method2()하거나 super.method2()재정의 된 버전 내에서 호출 하지 마십시오 .


8

this실제로 "사용중인 객체의 현재 실행중인 인스턴스"를 참조 하는 키워드를 사용하고 있습니다. 즉, this.method2();수퍼 클래스 를 호출 하고 있습니다. 즉, 객체에서 method2 ()를 호출합니다. re using, 이는 SubClass입니다.


8
사실이며 사용 this하지 않는 것도 도움이되지 않습니다. 무조건 호출은 암시 적으로 사용this
Sean Patrick Floyd

3
왜 이것이 찬성 되는가? 이것은이 질문에 대한 답이 아닙니다. 작성 method2()하면 컴파일러에 this.method2(). 따라서 제거하더라도 this여전히 작동하지 않습니다. 무엇 @Sean 패트릭 플로이드가 말하는 것은 정확
Shervin 아스 가리

4
그는 아무것도 잘못 말하는 아니에요 @Shervin, 그는 당신이 떠날 경우 어떻게되는지 명확하게 아니에요this
숀 패트릭 플로이드

4
대답은 this"콘크리트 실행 인스턴스 클래스"(런타임에 알려짐)를 참조하고 "현재 컴파일 단위 클래스"(키워드가 사용되는 곳, 컴파일 시간). 그러나 (Shervin이 지적한 것처럼) 오해의 소지가있을 수도 있습니다 this. 일반 메서드 호출로 암시 적으로 참조됩니다. method2();같은 그대로this.method2();
leonbloy

7

이렇게 생각해

+----------------+
|     super      |
+----------------+ <-----------------+
| +------------+ |                   |
| |    this    | | <-+               |
| +------------+ |   |               |
| | @method1() | |   |               |
| | @method2() | |   |               |
| +------------+ |   |               |
|    method4()   |   |               |
|    method5()   |   |               |
+----------------+   |               |
    We instantiate that class, not that one!

하위 클래스를 약간 왼쪽으로 이동하여 그 아래에 무엇이 있는지 보여 드리겠습니다 ... (저는 ASCII 그래픽을 좋아합니다)

We are here
        |
       /  +----------------+
      |   |     super      |
      v   +----------------+
+------------+             |
|    this    |             |
+------------+             |
| @method1() | method1()   |
| @method2() | method2()   |
+------------+ method3()   |
          |    method4()   |
          |    method5()   |
          +----------------+

Then we call the method
over here...
      |               +----------------+
 _____/               |     super      |
/                     +----------------+
|   +------------+    |    bar()       |
|   |    this    |    |    foo()       |
|   +------------+    |    method0()   |
+-> | @method1() |--->|    method1()   | <------------------------------+
    | @method2() | ^  |    method2()   |                                |
    +------------+ |  |    method3()   |                                |
                   |  |    method4()   |                                |
                   |  |    method5()   |                                |
                   |  +----------------+                                |
                   \______________________________________              |
                                                          \             |
                                                          |             |
...which calls super, thus calling the super's method1() here, so that that
method (the overidden one) is executed instead[of the overriding one].

Keep in mind that, in the inheritance hierarchy, since the instantiated
class is the sub one, for methods called via super.something() everything
is the same except for one thing (two, actually): "this" means "the only
this we have" (a pointer to the class we have instantiated, the
subclass), even when java syntax allows us to omit "this" (most of the
time); "super", though, is polymorphism-aware and always refers to the
superclass of the class (instantiated or not) that we're actually
executing code from ("this" is about objects [and can't be used in a
static context], super is about classes).

즉, Java 언어 사양 에서 인용 :

양식 super.IdentifierIdentifier현재 객체의 이름 이 지정된 필드를 참조 하지만 현재 객체는 현재 클래스의 수퍼 클래스 인스턴스로 표시됩니다.

양식 T.super.Identifier은에 해당 Identifier하는 어휘 적으로 둘러싸는 인스턴스의 이름 이 지정된 필드를 참조 T하지만 해당 인스턴스는의 수퍼 클래스 인스턴스로 표시 T됩니다.

평신도의 용어로, this기본적으로 객체 (* the ** 객체, 변수에서 이동할 수있는 것과 동일한 객체), 인스턴스화 된 클래스의 인스턴스, 데이터 도메인의 일반 변수입니다. super실행하고자하는 차용 된 코드 블록에 대한 포인터와 같으며, 단순한 함수 호출과 비슷하며 호출되는 클래스와 관련이 있습니다.

따라서 super수퍼 클래스에서 사용 하는 경우 수 퍼듀 퍼 클래스 [조부모]에서 코드가 실행되는 반면, 수퍼 클래스에서 사용 this(또는 암시 적으로 사용되는 경우)하면 하위 클래스를 계속 가리 킵니다 (아무도 변경하지 않았기 때문에-그리고 아무도 할 수 있었다).


2

superClass.method1이 subClass.method2를 호출하지 않도록하려면 method2를 재정의 할 수 없도록 private로 설정하십시오.

다음은 제안입니다.

public class SuperClass {

  public void method1() {
    System.out.println("superclass method1");
    this.internalMethod2();
  }

  public void method2()  {
    // this method can be overridden.  
    // It can still be invoked by a childclass using super
    internalMethod2();
  }

  private void internalMethod2()  {
    // this one cannot.  Call this one if you want to be sure to use
    // this implementation.
    System.out.println("superclass method2");
  }

}

public class SubClass extends SuperClass {

  @Override
  public void method1() {
    System.out.println("subclass method1");
    super.method1();
  }

  @Override
  public void method2() {
    System.out.println("subclass method2");
  }
}

이런 식으로 작동하지 않는다면 다형성은 불가능할 것입니다 (적어도 절반도 유용하지 않습니다).


2

재정의되는 메서드를 피하는 유일한 방법은 super 키워드를 사용하는 것이므로 SuperClass 에서 다른 새 Base 클래스 로 method2 ()를 이동 한 다음 SuperClass 에서 호출 할 수 있다고 생각했습니다 .

class Base 
{
    public void method2()
    {
        System.out.println("superclass method2");
    }
}

class SuperClass extends Base
{
    public void method1()
    {
        System.out.println("superclass method1");
        super.method2();
    }
}

class SubClass extends SuperClass
{
    @Override
    public void method1()
    {
        System.out.println("subclass method1");
        super.method1();
    }

    @Override
    public void method2()
    {
        System.out.println("subclass method2");
    }
}

public class Demo 
{
    public static void main(String[] args) 
    {
        SubClass mSubClass = new SubClass();
        mSubClass.method1();
    }
}

산출:

subclass method1
superclass method1
superclass method2

2

this 항상 현재 실행중인 개체를 참조합니다.

여기에 요점을 더 설명하기 위해 간단한 스케치가 있습니다.

+----------------+
|  Subclass      |
|----------------|
|  @method1()    |
|  @method2()    |
|                |
| +------------+ |
| | Superclass | |
| |------------| |
| | method1()  | |
| | method2()  | |
| +------------+ |
+----------------+

상자 Subclass내부, 심지어 Superclass'영역' 으로 모험을 할 때마다 외부 상자, 개체 의 인스턴스가있는 경우 여전히 외부 상자의 인스턴스입니다.

더욱이이 프로그램에는 세 개의 클래스에서 생성되는 객체가 하나 this뿐 이므로 한 가지만 참조 할 수 있습니다.

여기에 이미지 설명 입력

상기 실시 형태에서 나타낸 넷빈즈 '힙 보행자.


2
class SuperClass
{
    public void method1()
    {
        System.out.println("superclass method1");
        SuperClass se=new SuperClass();
        se.method2();
    }

    public void method2()
    {
        System.out.println("superclass method2");
    }
}


class SubClass extends SuperClass
{
    @Override
    public void method1()
    {
        System.out.println("subclass method1");
        super.method1();
    }

    @Override
    public void method2()
    {
        System.out.println("subclass method2");
    }
}

부름

SubClass mSubClass = new SubClass();
mSubClass.method1();

출력

서브 클래스 method1
수퍼 클래스 method1
수퍼 클래스 method2


1

나는 당신이 그것을 직접 할 수 있다고 믿지 않습니다. 한 가지 해결 방법은 수퍼 클래스에서 method2의 개인 내부 구현을 갖고이를 호출하는 것입니다. 예를 들면 :

public class SuperClass
{
    public void method1()
    {
        System.out.println("superclass method1");
        this.internalMethod2();
    }

    public void method2()
    {
        this.internalMethod2(); 
    }
    private void internalMethod2()
    {
        System.out.println("superclass method2");
    }

}

1

"this"키워드는 현재 클래스 참조를 나타냅니다. 즉, 메서드 내에서 사용되는 경우 'current'클래스는 여전히 SubClass이므로 답을 설명합니다.


1

요약하면, 이것은 현재 객체를 가리키며 자바의 메소드 호출은 본질적으로 다형성입니다. 따라서 실행을위한 메소드 선택은 전적으로 이것에 의해 가리키는 객체에 달려 있습니다. 따라서 부모 클래스에서 method2 () 메서드를 호출하면 자식 클래스의 method2 ()가 호출되므로 자식 클래스의 객체를 가리 킵니다. 이 정의는 사용되는 클래스에 관계없이 변경되지 않습니다.

추신. 메서드와 달리 클래스의 멤버 변수는 다형성이 아닙니다.


0

비슷한 사례를 조사하는 동안 하위 클래스 메서드의 스택 추적을 확인하여 호출이 어디에서 오는지 알아내는 것으로 끝났습니다. 그렇게하는 더 현명한 방법이있을 것입니다. 그러나 그것은 저에게 효과적이며 역동적 인 접근 방식입니다.

public void method2(){
        Exception ex=new Exception();
        StackTraceElement[] ste=ex.getStackTrace();
        if(ste[1].getClassName().equals(this.getClass().getSuperclass().getName())){
            super.method2();
        }
        else{
            //subclass method2 code
        }
}

사건에 대한 해결책을 찾는 것이 합리적이라고 생각합니다. 물론 스레드에서 이미 언급했듯이 다른 메서드 이름이나 다른 매개 변수 유형으로 문제를 해결하는 방법이 있지만 제 경우에는 다른 메서드 이름으로 혼동하고 싶지 않습니다.


이 코드는 위험하고 위험하며 비용이 많이 듭니다. 예외를 만들려면 VM이 전체 스택 추적을 구성해야하며 전체 서명이 아닌 이름 만 비교하면 오류가 발생하기 쉽습니다. 또한 거대한 디자인 결함이 있습니다.
M. le Rutte

성능 관점에서 내 코드는 'new HashMap (). size ()'보다 더 많은 영향을 미치지 않는 것 같습니다. 그러나 나는 당신이 생각했던 우려를 간과했을 수 있으며 VM 전문가가 아닙니다. 클래스 이름을 비교해 보면 의심 스러울 수 있지만 여기에는 부모 클래스라는 확신을주는 패키지가 포함되어 있습니다. 어쨌든, 대신 서명을 비교하는 아이디어가 마음에 듭니다. 어떻게 하시겠습니까? 일반적으로 호출자가 수퍼 클래스인지 또는 다른 사람인지 확인하는 더 부드러운 방법이 있다면 여기에 대해 감사하겠습니다.
Beat Siegrist

호출자가 슈퍼 클래스인지 여부를 확인해야한다면 재 설계가 이루어지면 더 오래 진지하게 생각할 것입니다. 이것은 안티 패턴입니다.
M. le Rutte

나는 요점을 보았지만 스레드의 일반적인 요청은 합리적입니다. 어떤 상황에서는 슈퍼 클래스 메서드 호출이 중첩 된 메서드 호출과 함께 슈퍼 클래스 컨텍스트에 유지되는 것이 합리적 일 수 있습니다. 그러나 Superclass에서 그에 따라 메서드 호출을 지시 할 방법이없는 것 같습니다.
Beat Siegrist

0

제기 된 질문의 출력을 더욱 확장하면 액세스 지정자 및 재정의 동작에 대한 더 많은 통찰력을 얻을 수 있습니다.

            package overridefunction;
            public class SuperClass 
                {
                public void method1()
                {
                    System.out.println("superclass method1");
                    this.method2();
                    this.method3();
                    this.method4();
                    this.method5();
                }
                public void method2()
                {
                    System.out.println("superclass method2");
                }
                private void method3()
                {
                    System.out.println("superclass method3");
                }
                protected void method4()
                {
                    System.out.println("superclass method4");
                }
                void method5()
                {
                    System.out.println("superclass method5");
                }
            }

            package overridefunction;
            public class SubClass extends SuperClass
            {
                @Override
                public void method1()
                {
                    System.out.println("subclass method1");
                    super.method1();
                }
                @Override
                public void method2()
                {
                    System.out.println("subclass method2");
                }
                // @Override
                private void method3()
                {
                    System.out.println("subclass method3");
                }
                @Override
                protected void method4()
                {
                    System.out.println("subclass method4");
                }
                @Override
                void method5()
                {
                    System.out.println("subclass method5");
                }
            }

            package overridefunction;
            public class Demo 
            {
                public static void main(String[] args) 
                {
                    SubClass mSubClass = new SubClass();
                    mSubClass.method1();
                }
            }

            subclass method1
            superclass method1
            subclass method2
            superclass method3
            subclass method4
            subclass method5
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.