왜 super.super.method (); 자바에서는 허용되지 않습니까?


360

이 질문을 읽고 글을 쓸 수 있다면 쉽게 해결할 수 있다고 생각했습니다 (없이 해결할 수는 없음).

@Override
public String toString() {
    return super.super.toString();
}

나는 그것이 많은 경우에 유용한 지 잘 모르겠지만 그것이 그렇지 않은지 궁금 하고 다른 언어로 이런 것이 존재 하는지 궁금 합니다.

너희들은 어떻게 생각하니?

편집 : 명확하게 : 네, 알고 있습니다 .Java에서는 불가능하며 실제로 놓치지 않습니다. 이것은 내가 작동 할 것으로 예상되는 것이 아니며 컴파일러 오류가 발생하는 것에 놀랐습니다. 방금 아이디어가 있었고 토론하고 싶습니다.


7
전화를 걸고 싶다면 super.super.toString()클래스를 확장하여 모든 기능 (일부는 아님) 을 수락하기로 결정할 때 자신의 결정과 모순됩니다 .
DayaMoon

답변:


484

캡슐화를 위반합니다. 부모 클래스의 행동을 우회해서는 안됩니다. 부모님이 아닌 자신의 클래스의 행동 (특히 같은 방법 내에서) 을 우회 할 수있는 경우가 있습니다 . 예를 들어, 기본 "항목 모음", "빨간색 항목 모음"을 나타내는 하위 클래스 및 "큰 빨간색 항목 모음"을 나타내는 하위 클래스가 있다고 가정합니다. 다음을 갖는 것이 합리적입니다.

public class Items
{
    public void add(Item item) { ... }
}

public class RedItems extends Items
{
    @Override
    public void add(Item item)
    {
        if (!item.isRed())
        {
            throw new NotRedItemException();
        }
        super.add(item);
    }
}

public class BigRedItems extends RedItems
{
    @Override
    public void add(Item item)
    {
        if (!item.isBig())
        {
            throw new NotBigItemException();
        }
        super.add(item);
    }
}

좋습니다. RedItems는 항상 포함 된 항목이 모두 빨간색임을 확신 할 수 있습니다. 이제 우리는 생각 했다 super.super.add ()를 호출 할 수 :

public class NaughtyItems extends RedItems
{
    @Override
    public void add(Item item)
    {
        // I don't care if it's red or not. Take that, RedItems!
        super.super.add(item);
    }
}

이제 우리는 원하는 것을 추가 할 수 있고 RedItems 은 깨졌습니다.

말이 돼?


38
좋은 예입니다. 그러나 기본 클래스가 항목을 수락 할 때 디자인이 좋지 않다고 생각했지만 파생 클래스는 기본 클래스의 드롭 인 대체품으로 사용될 수 없기 때문에 파생 클래스가 거부합니다 (치환 원칙 위반). 올바른 생각입니까, 아니면 그러한 계층이 깨끗합니까?
Johannes Schaub-litb

5
상속에 대한 구성을 의미합니까? 이 예제는 상속을 피할 이유가 아닙니다. 다른 것들도 많이 있습니다.
Jon Skeet

3
@Tim : 이것은 캡슐화 위반에 대한 이해하기 쉬운 예일뿐입니다. 대신 속성을 설정했을 수 있습니다. 또한 모든 측면이 유형 수준에서 보이지는 않습니다. 제네릭은 모든 것에 대한 답이 아닙니다.
Jon Skeet

12
@ JohannesSchaub-litb 하나는 Item 계약을 코딩하고 RedItems 인스턴스를 사용하면 예기치 않은 NotRedItemException이 발생하기 때문에 Liskov 대체 원칙을 위반한다고 생각합니다. 나는 항상 서브 클래스가 수퍼 입력 세트를 가져 와서 출력 서브 세트를 반환해야한다고 배웠습니다. 즉, 하위 클래스는 수퍼 클래스에 유효한 입력을 거부하거나 수퍼 클래스가 생성 할 수없는 출력을 생성해서는 안됩니다. 여기에는 수퍼 클래스가 던지지 않는 오류가 발생하는 것이 포함됩니다.
Konstantin Tarashchanskiy

4
@piechuckerr : 때때로 책, 때로는 블로그 게시물, 때로는 경험 ...
Jon Skeet

72

Jon Skeet이 정답이라고 생각합니다. 캐스팅을 통해 수퍼 클래스의 수퍼 클래스에서 음영 처리 된 변수에 액세스 할 있다고 덧붙이고 싶습니다 this.

interface I { int x = 0; }
class T1 implements I { int x = 1; }
class T2 extends T1 { int x = 2; }
class T3 extends T2 {
        int x = 3;
        void test() {
                System.out.println("x=\t\t"          + x);
                System.out.println("super.x=\t\t"    + super.x);
                System.out.println("((T2)this).x=\t" + ((T2)this).x);
                System.out.println("((T1)this).x=\t" + ((T1)this).x);
                System.out.println("((I)this).x=\t"  + ((I)this).x);
        }
}

class Test {
        public static void main(String[] args) {
                new T3().test();
        }
}

출력을 생성합니다.

x = 3
super.x = 2
((T2) this) .x = 2
((T1) this) .x = 1
((I) this) .x = 0

( JLS의 예 )

그러나 메서드 호출은 개체의 런타임 유형에 따라 결정되므로 메서드 호출에는 작동하지 않습니다.


6
수퍼 클래스에있는 것과 같은 이름의 변수가 있고 어떤 이유로 든 변경할 수 없거나 변경할 수없는 경우에도 동일한 이름으로 수퍼 클래스의 변수에 액세스 할 수 있습니다. 용도가 무엇입니까? 글쎄 ... 난 한번도 사용한 적이 없어
Michael Myers

표현식 문서에 대한 훌륭한 링크. OCPJP를 위해 공부하는 사람들에게 좋은 예입니다.
Gordon

대박. 나는 캐스트 사용을 생각하지 않았을 것입니다.
Thomas Eding

클래스 T1이 변수 x를 어떻게 재정의합니까? 기본적으로 정적 인 파이널?
amarnath

@amarnathharish : 재정의되지 않았으며 기본적으로 필드는 최종적으로 패키지로 보호됩니다.
Michael Myers

41

다음 코드는 대부분의 경우 super.super ... super.method ()를 사용할 수 있다고 생각합니다. (추천하지 않더라도)

한마디로

  1. 조상 유형의 임시 인스턴스 생성
  2. 원래 객체에서 임시 객체로 필드 값 복사
  3. 임시 객체에서 대상 메소드 호출
  4. 수정 된 값을 원래 객체로 다시 복사

사용법 :

public class A {
   public void doThat() { ... }
}

public class B extends A {
   public void doThat() { /* don't call super.doThat() */ }
}

public class C extends B {
   public void doThat() {
      Magic.exec(A.class, this, "doThat");
   }
}


public class Magic {
    public static <Type, ChieldType extends Type> void exec(Class<Type> oneSuperType, ChieldType instance,
            String methodOfParentToExec) {
        try {
            Type type = oneSuperType.newInstance();
            shareVars(oneSuperType, instance, type);
            oneSuperType.getMethod(methodOfParentToExec).invoke(type);
            shareVars(oneSuperType, type, instance);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    private static <Type, SourceType extends Type, TargetType extends Type> void shareVars(Class<Type> clazz,
            SourceType source, TargetType target) throws IllegalArgumentException, IllegalAccessException {
        Class<?> loop = clazz;
        do {
            for (Field f : loop.getDeclaredFields()) {
                if (!f.isAccessible()) {
                    f.setAccessible(true);
                }
                f.set(target, f.get(source));
            }
            loop = loop.getSuperclass();
        } while (loop != Object.class);
    }
}

10
리플렉션을 사용하면 무엇이든 할 수 있습니다. :) 문자열을 변경 가능하게 만들 수도 있습니다.
BalusC

23
정말 끔찍한 일이야! 나는 당신에게 그것을하는 방법을 알아 내기 위해 당신에게 +1을 줄 것이다 :)
Larry Watanabe

6
좋은 트릭이지만 항상 불가피하지만 필요한 super.super를 호출하는 것과 동등한 것은 아닙니다 .super.super 호출은 C (C + B + A)의 컨텍스트를 전달하지만 응답은 인스턴스를 생성하기 때문입니다. 예를 들어 getContext ()라고하는 모든 doThat과 getContext가 각 클래스에서 다르게 구현 된 경우이 답변은 작동하지 않습니다. 귀하의 답변에 A의 getContext ()를 사용하는 반면 사용할 수없는 super.super를 호출하면 C의 getContext를 사용하게됩니다.
inor

흠. 어떤 경우에는 동적 프록시 ( javahowto.blogspot.co.uk/2011/12/… )로 메소드 반대 의견을 극복하고 메소드 호출을 원래 객체로 리디렉션 (각 호출 후 변수 동기화) 할 수 있습니까? 그러나 프록시는 모든 것이 인터페이스를 구현해야한다고 생각합니다. 또한, 수퍼 수퍼 클래스가 특히 수퍼 수퍼 메소드 중 하나를 호출 할 수 있는지 궁금해하며,이를 재지
정하지 않아도

또 다른 의견에서 차단 super.super.은 프로그래머가 해결 방법을 추구하면서 발을 밟을 수있는 새롭고 복잡하고 심오한 방법을 찾도록 초대 한다고 말했습니다. 이는 완벽한 예입니다. 그들이 개인적으로 그리고 문자 그대로 당신을 발로 쏠 것입니다. +1
Braden Best

11

의견이 충분하지 않아 다른 답변에 추가 할 것입니다.

Jon Skeet은 아름다운 예를 들어 훌륭하게 대답합니다. Matt B는 요점을 가지고 있습니다. 모든 수퍼 클래스에 수퍼 클래스가있는 것은 아닙니다. 수퍼가없는 수퍼의 수퍼를 호출하면 코드가 손상됩니다.

객체 지향 프로그래밍 (Java)은 함수가 아니라 객체에 관한 것입니다. 작업 지향 프로그래밍을 원한다면 C ++ 또는 다른 것을 선택하십시오. 객체가 수퍼 클래스에 맞지 않으면 "조부모 클래스"에 추가하거나 새 클래스를 만들거나 다른 수퍼 클래스를 찾아야합니다.

개인적으로, 나는이 제한이 Java의 가장 큰 강점 중 하나라는 것을 알았습니다. 코드는 내가 사용한 다른 언어에 비해 다소 엄격하지만 항상 기대할 사항을 알고 있습니다. 이는 Java의 "단순하고 친숙한"목표에 도움이됩니다. 내 마음에, super.super를 호출하는 것은 간단하거나 친숙하지 않습니다. 아마도 개발자들은 같은 느낌을 받았습니까?


3
"모든 수퍼 클래스에 수퍼 클래스가있는 것은 아닙니다"라고 말합니다. 음, java.lang.Object를 제외한 모든 것은 "널"을 줄 수 있습니다. 그래서 거의 모든 사람들이 수퍼를 가지고 있다고 말할 것입니다.
Tim Büthe

응용 프로그램 프로그래머가 작성하는 모든 클래스에는 "super"가 있습니다 (java.lang.Object는 작성하지 않지만 응용 프로그램 프로그래머는 작성하지 않습니다).
finnw

2
수퍼가 너무 많으면 super.super.super ... super 컴파일 시간 오류를 만들어 쉽게 해결할 수 있습니다. Java가 공용 상속만을 가지고 있다고 생각하면 누군가 상속 계층을 변경하면 인터페이스를 변경하게됩니다. 그래서 나는 super ^ n이 무섭다는 것을 걱정하지 않을 것입니다.
Thomas Eding

7

이를 수행해야 할 몇 가지 이유가 있습니다. 잘못 구현 된 메소드가있는 서브 클래스가있을 수 있지만 상위 메소드가 올바르게 구현되었습니다. 타사 라이브러리에 속하기 때문에 소스를 변경하지 않거나 원하지 않을 수 있습니다. 이 경우 서브 클래스를 작성하지만 하나의 메소드를 대체하여 super.super 메소드를 호출하려고합니다.

다른 포스터에서 볼 수 있듯이 반사를 통해이 작업을 수행 할 수 있지만 다음과 같은 작업을 수행 할 수 있어야합니다

(SuperSuperClass this) .theMethod ();

나는이 문제를 지금 다루고 있습니다-빠른 수정은 수퍼 클래스 메소드를 복사하여 서브 서브 클래스 메소드에 붙여 넣는 것입니다 :)


1
메소드를 호출하기 전에 캐스트해도 위임 된 메소드는 변경되지 않습니다. 항상 사용되는 서브 클래스 구현입니다. 예를 들어 stackoverflow.com/questions/1677993/…
Joshua Goldberg

1
@Larry 이것은 내가 처한 상황과 내가 사용한 수정 사항입니다. 좋은 전화!
bcr

필요한 메소드의 코드가 있으면 필요한 모든 필드를 열고 서브 클래스에서이 코드를 실행할 수 있습니다.
Enyby

6

다른 사람들이 만든 좋은 점 외에도 다른 이유가 있다고 생각합니다. 슈퍼 클래스에 슈퍼 클래스가 없으면 어떻게됩니까?

모든 클래스는 자연적으로 (적어도) 확장 때문에 Object, super.whatever()항상 슈퍼 클래스의 메소드를 참조합니다. 당신의 클래스는 확장한다면 어떻게 Object- 무엇을 할 super.super다음을 참조하십시오? 컴파일러 오류, NullPointer 등의 동작을 어떻게 처리해야합니까?

이것이 허용되지 않는 주된 이유는 캡슐화를 위반하기 때문이지만 이것이 작은 이유 일 수 있다고 생각합니다.


4
분명히, 그것은 컴파일러 오류 일 것입니다-그리고 컴파일러가 가지고있는 정보입니다.
Michael Borgwardt

4

메서드를 덮어 쓰고 모든 수퍼 클래스 버전 (예 :와 같이 equals)을 원한다면 사실상 직접 수퍼 클래스 버전을 먼저 호출하고 싶습니다. 원하는 경우 수퍼 클래스 버전을 차례로 호출합니다. .

나는 임의의 수퍼 클래스 버전의 메소드를 호출하는 것이 거의 의미가 없다고 생각한다. Java에서 이것이 가능한지 모르겠습니다. C ++에서 수행 할 수 있습니다.

this->ReallyTheBase::foo();

6
누군가 하나의 메소드가 잘못 구현 된 서브 클래스를 작성한 경우 수퍼 클래스 메소드가 작업의 90 %를 수행하는 것이 좋습니다. 그런 다음 서브 클래스를 만들고 수퍼 클래스 수퍼 클래스 메소드를 호출하는 메소드를 대체하고 자신의 10 %를 추가하십시오.
래리 와타나베

3

자주 사용되지 않기 때문에 추측합니다. 내가 그것을 볼 수있는 유일한 이유는 직계 부모가 일부 기능을 재정의하고 원래 기능으로 다시 복원하려고하기 때문입니다.

클래스의 직계 부모는 조부모보다 클래스와 더 밀접하게 관련되어야하기 때문에 OO 원칙에 위배되는 것으로 보입니다.


3

Github의 프로젝트, 특히 objectHandle 변수입니다. 이 프로젝트는 손자에서 조부모 메서드를 실제로 정확하고 호출하는 방법을 보여줍니다.

링크가 끊어 질 경우를 대비하여 코드는 다음과 같습니다.

import lombok.val;
import org.junit.Assert;
import org.junit.Test;

import java.lang.invoke.*;

/*
Your scientists were so preoccupied with whether or not they could, they didn’t stop to think if they should.
Please don't actually do this... :P
*/
public class ImplLookupTest {
    private MethodHandles.Lookup getImplLookup() throws NoSuchFieldException, IllegalAccessException {
        val field = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP");
        field.setAccessible(true);
        return (MethodHandles.Lookup) field.get(null);
    }

    @Test
    public void test() throws Throwable {
        val lookup = getImplLookup();
        val baseHandle = lookup.findSpecial(Base.class, "toString",
            MethodType.methodType(String.class),
            Sub.class);
        val objectHandle = lookup.findSpecial(Object.class, "toString",
            MethodType.methodType(String.class),
            // Must use Base.class here for this reference to call Object's toString
            Base.class);
        val sub = new Sub();
        Assert.assertEquals("Sub", sub.toString());
        Assert.assertEquals("Base", baseHandle.invoke(sub));
        Assert.assertEquals(toString(sub), objectHandle.invoke(sub));
    }

    private static String toString(Object o) {
        return o.getClass().getName() + "@" + Integer.toHexString(o.hashCode());
    }

    public class Sub extends Base {
        @Override
        public String toString() {
            return "Sub";
        }
    }

    public class Base {
        @Override
        public String toString() {
            return "Base";
        }
    }
}

행복한 코딩 !!!!


당신의 과학자들은 그들이 할 수 있는지 아닌지에 너무 몰두했고, 그들이해야할지 생각하는 것을 멈추지 않았습니다. 실제로이 작업을 수행하지 마십시오 ... : P 🤔
Tim Büthe

그래, 그것들은 내가 아닌 코더의 말이다. 제 생각에는 언젠가 실제로 필요할지도 모른다고 생각합니다
kyay

2

가능한 경우 super.super 메소드 본문을 다른 메소드에 넣을 것입니다.

class SuperSuperClass {
    public String toString() {
        return DescribeMe();
    }

    protected String DescribeMe() {
        return "I am super super";
    }
}

class SuperClass extends SuperSuperClass {
    public String toString() {
        return "I am super";
    }
}

class ChildClass extends SuperClass {
    public String toString() {
        return DescribeMe();
    }
}

또는 수퍼 수퍼 클래스를 변경할 수 없으면 다음을 시도하십시오.

class SuperSuperClass {
    public String toString() {
        return "I am super super";
    }
}

class SuperClass extends SuperSuperClass {
    public String toString() {
        return DescribeMe(super.toString());
    }

    protected String DescribeMe(string fromSuper) {
        return "I am super";
    }
}

class ChildClass extends SuperClass {
    protected String DescribeMe(string fromSuper) {
        return fromSuper;
    }
}

두 경우 모두

new ChildClass().toString();

"나는 슈퍼 슈퍼"결과


나는 SuperSuperClass와 ChildClass를 소유하지만 SuperClass는 소유하지 않은 상황에서 자신을 발견했기 때문에 첫 번째 솔루션이 유용하다는 것을 알았습니다.
xofon


1
public class A {

     @Override
     public String toString() {
          return "A";
     }

}


public class B extends A {

     @Override
     public String toString() {
          return "B";
     }

}

public class C extends B {

     @Override
     public String toString() {
          return "C";
     }

}


public class D extends C {

     @Override
     public String toString() {
          String result = "";
          try {
                result = this.getClass().getSuperclass().getSuperclass().getSuperclass().newInstance().toString();
          } catch (InstantiationException ex) {
                Logger.getLogger(D.class.getName()).log(Level.SEVERE, null, ex);
          } catch (IllegalAccessException ex) {
                Logger.getLogger(D.class.getName()).log(Level.SEVERE, null, ex);
          }
          return result;
     }

}

public class Main {

     public static void main(String... args) {
          D d = new D();
          System.out.println(d);

     }
}

실행 : BUILD SUCCESSFUL (총 시간 : 0 초)


1
나는 알지만, 당신은 새로운 인스턴스를 만들고 있으므로 객체에 상태가 있으면 작동하지 않을 것입니다.
Tim Büthe

1

기본 클래스의 코드를 변경할 수없는 경우 super.super.method () 호출이 의미가 있습니다. 기존 라이브러리를 확장 할 때 종종 발생합니다.

먼저 스스로에게 물어보십시오. 왜 수업을 확장합니까? 대답이 "변경할 수 없기 때문에"인 경우 응용 프로그램에서 정확한 패키지 및 클래스를 만들고 잘못된 메서드를 다시 작성하거나 대리인을 만들 수 있습니다.

package com.company.application;

public class OneYouWantExtend extends OneThatContainsDesiredMethod {

    // one way is to rewrite method() to call super.method() only or 
    // to doStuff() and then call super.method()

    public void method() {
        if (isDoStuff()) {
            // do stuff
        }
        super.method();
    }

    protected abstract boolean isDoStuff();


    // second way is to define methodDelegate() that will call hidden super.method()

    public void methodDelegate() {
        super.method();
    }
    ...
}

public class OneThatContainsDesiredMethod {

    public void method() {...}
    ...
}

예를 들어, 응용 프로그램에서 org.springframework.test.context.junit4.SpringJUnit4ClassRunner 클래스를 만들 수 있으므로 jar에서 실제 클래스보다 먼저이 클래스를로드해야합니다. 그런 다음 메소드 또는 생성자를 다시 작성하십시오.

주의 : 이것은 절대적인 해킹이므로 사용하지 않는 것이 좋지만 작동 중입니다! 이 방법의 사용은 클래스 로더에 가능한 문제 때문에 위험합니다. 또한 덮어 쓴 클래스가 포함 된 라이브러리를 업데이트 할 때마다 문제가 발생할 수 있습니다.


1

아키텍처가 여러 파생 클래스 대신 구현되는 공통 CustomBaseClass에서 공통 기능을 빌드하는 경우와 같은 상황이 있습니다. 그러나 특정 파생 클래스의 특정 메서드에 대한 공통 논리를 우회해야합니다. 이러한 경우 super.super.methodX 구현을 사용해야합니다.

우리는 CustomBaseClass에 boolean 멤버를 도입하여이를 구현합니다. 이는 커스텀 구현을 선택적으로 지연시키고 원하는 경우 기본 프레임 워크 구현을 생성하는 데 사용할 수 있습니다.

        ...
        FrameworkBaseClass (....) extends...
        {
           methodA(...){...}
           methodB(...){...}
        ...
           methodX(...)
        ...
           methodN(...){...}

        }
        /* CustomBaseClass overrides default framework functionality for benefit of several derived classes.*/
        CustomBaseClass(...) extends FrameworkBaseClass 
        {
        private boolean skipMethodX=false; 
        /* implement accessors isSkipMethodX() and setSkipMethodX(boolean)*/

           methodA(...){...}
           methodB(...){...}
        ...
           methodN(...){...}

           methodX(...){
                  if (isSkipMethodX()) {
                       setSKipMethodX(false);
                       super.methodX(...);
                       return;
                       }
                   ... //common method logic
            }
        }

        DerivedClass1(...) extends CustomBaseClass
        DerivedClass2(...) extends CustomBaseClass 
        ...
        DerivedClassN(...) extends CustomBaseClass...

        DerivedClassX(...) extends CustomBaseClass...
        {
           methodX(...){
                  super.setSKipMethodX(true);
                  super.methodX(...);
                       }
        }

그러나 프레임 워크와 앱에서 좋은 아키텍처 원칙을 준수하면 isA 방식 대신 hasA 방식을 사용하여 이러한 상황을 쉽게 피할 수 있습니다. 그러나 항상 제대로 설계된 아키텍처를 기대하는 것은 그리 실용적이지 않으므로 견고한 디자인 원칙에서 벗어나 이와 같은 핵을 도입해야 할 필요성이 있습니다. 내 2 센트 만 ...


1

@ Jon Skeet 좋은 설명입니다. 누군가가 super.super 메소드를 호출하려는 경우 IMO는 직계 부모의 행동을 무시하고 싶지만 할아버지의 부모 행동에 액세스하려고합니다. 이는 인스턴스 Of를 통해 달성 할 수 있습니다. 아래 코드와 같이

public class A {
    protected void printClass() {
        System.out.println("In A Class");
    }
}

public class B extends A {

    @Override
    protected void printClass() {
        if (!(this instanceof C)) {
            System.out.println("In B Class");
        }
        super.printClass();
    }
}

public class C extends B {
    @Override
    protected void printClass() {
        System.out.println("In C Class");
        super.printClass();
    }
}

여기 드라이버 클래스가 있습니다.

public class Driver {
    public static void main(String[] args) {
        C c = new C();
        c.printClass();
    }
}

이것의 출력은

In C Class
In A Class

이 경우 클래스 B printClass 동작은 무시됩니다. 이것이 super.super를 달성하기위한 이상적인 또는 좋은 방법인지 확실하지 않지만 여전히 작동합니다.


1
글쎄, 그것은 창의적이지만 실제로 내 질문에 대답하지는 않습니다. C는 여전히 super.super를 호출하지 않고 B는 다르게 동작합니다. A와 B를 변경할 수 있다면 instanceof를 사용하는 대신 다른 방법을 추가하면됩니다. Super.super.foo는 A와 B에 액세스 할 수없고 변경할 수없는 경우에 도움이됩니다.
Tim Büthe

@TimButhe에 동의하십시오. 그러나 super.super를 호출하려면 의도적으로 부모 클래스의 동작을 무시하고 싶기 때문에 기존의 Java 구문으로 해당 작업을 수행하면됩니다. (옵션 없음 당신도 instanceof를 / 다른 방법을 원하는)
산 제이 자이나교

0

슈퍼 클래스가 필요하다고 생각되면 해당 클래스의 변수에서 슈퍼 클래스를 참조 할 수 있습니다. 예를 들면 다음과 같습니다.

public class Foo
{
  public int getNumber()
  {
    return 0;
  }
}

public class SuperFoo extends Foo
{
  public static Foo superClass = new Foo();
  public int getNumber()
  {
    return 1;
  }
}

public class UltraFoo extends Foo
{
  public static void main(String[] args)
  {
    System.out.println(new UltraFoo.getNumber());
    System.out.println(new SuperFoo().getNumber());
    System.out.println(new SuperFoo().superClass.getNumber());
  }
  public int getNumber()
  {
    return 2;
  }
}

인쇄해야합니다 :

2
1
0

2
정적 메소드를 사용하기 때문에 exmaple은 일종의 ... 잘 나쁩니다. 정적 메소드를 사용할 때 변수 또는 수퍼가 전혀 필요하지 않습니다. 어쩌면 여기서 기본적인 OO 개념을 놓쳤을 수도 있습니다. 공감해야합니다. 죄송합니다.
Tim Büthe

1
정적 메소드없이 쉽게 수행 할 수있었습니다. 충분히 간단합니다. 이 작업을 수행하는 방법에 대한 간단한 예를 보았습니다.
Ashtheking

필드에 슈퍼 변수를 저장하는 것이 이것을 해결하는 한 가지 방법입니다. 그러나 정적 메소드 인 경우 변수가 필요하지 않으며 그냥 사용할 수 있습니다. 둘째, 변수 하나에 정적 메소드를 호출하는 것은 나쁜 습관이며 대부분의 IDE는 이에 대해 경고합니다. 정적 인 내용을 제거하고 답을 고치면 내 공감대를 제거하고 기꺼이 제거 할 수 있습니다.
Tim Büthe 2016 년

당신은 가까이 있지만 코드는 컴파일되지 않습니다. 기본 메소드의 정적 컨텍스트에서 getNumber에 액세스하려고합니다. 실제로 이것을 컴파일하려고 했습니까? (그리고 UltraFoo가 SuperFoo를 확장하지 않아야합니까?)
Tim Büthe

나는 정말로 당신에게 잔인하고 싶지 않지만 new UltraFoo.getNumber()괄호를 놓친 이후 컴파일하지 않을 것입니다. 그러나 코드의 개념이 분명하기 때문에 돈 보트를 제거했습니다. 감사합니다!
Tim Büthe

0

IMO super.super.sayYourName()는 Java에서 동작 을 수행하는 깔끔한 방법 입니다.

public class GrandMa {  
    public void sayYourName(){  
        System.out.println("Grandma Fedora");  
    }  
}  

public class Mama extends GrandMa {  
    public void sayYourName(boolean lie){  
        if(lie){   
            super.sayYourName();  
        }else {  
            System.out.println("Mama Stephanida");  
        }  
    }  
}  

public class Daughter extends Mama {  
    public void sayYourName(boolean lie){  
        if(lie){   
            super.sayYourName(lie);  
        }else {  
            System.out.println("Little girl Masha");  
        }  
    }  
}  

public class TestDaughter {
    public static void main(String[] args){
        Daughter d = new Daughter();

        System.out.print("Request to lie: d.sayYourName(true) returns ");
        d.sayYourName(true);
        System.out.print("Request not to lie: d.sayYourName(false) returns ");
        d.sayYourName(false);
    }
}

산출:

Request to lie: d.sayYourName(true) returns Grandma Fedora
Request not to lie: d.sayYourName(false) returns Little girl Masha


아, 그래서 당신은 이와 같은 클래스 계층 구현을 옹호하고 있습니까? 불행히도, Baby (Daughter 's subclass)의 Mama 메소드에 액세스하려는 경우 이것은 정말 지저분 해지 기 시작합니다 ...
bcr

yakov fain이 옳습니다. 다시 말해, 원래의 질문은 super.super에서 재정의 된 메서드를 호출하는 것에 대한 것이기 때문에 좋은 예가 아닙니다.
inor

0

이것이 상속 상속 계약을 위반하는 문제라고 생각합니다. 전화 를 걸 때
자신의 행동, 복종, 행동에 복종 / 동의하는 수업을 확장함으로써 자신의 복종 계약을 위반하고 싶을 것입니다.
super.super.method()

당신은 슈퍼 클래스에서 체리를 선택할 수 없습니다 .

그러나 호출해야 할 필요가 있다고 생각되는 상황이 발생할 수 있습니다. super.super.method()일반적으로 잘못된 디자인 부호, 코드 또는 상속 한 코드에 있습니다! 슈퍼슈퍼 슈퍼
라면 클래스는 다음 상속을 통해 구성을 선택, (일부 레거시 코드)를 리팩토링 할 수 없습니다.

캡슐화 끊기는 캡슐화 된 코드를 깨뜨려 일부 메소드 를 오버라이드 할 때 입니다. 재정의되지 않도록 설계된 방법은 final 로 표시 됩니다.


0

C #에서는 다음과 같은 조상의 메소드를 호출 할 수 있습니다.

public class A
    internal virtual void foo()
...
public class B : A
    public new void foo()
...
public class C : B
    public new void foo() {
       (this as A).foo();
    }

또한 당신은 델파이에서 이것을 할 수 있습니다 :

type
   A=class
      procedure foo;
      ...
   B=class(A)
     procedure foo; override;
     ...
   C=class(B)
     procedure foo; override;
     ...
A(objC).foo();

그러나 Java에서는 일부 장비로만 이러한 초점을 맞출 수 있습니다. 한 가지 가능한 방법은 다음과 같습니다.

class A {               
   int y=10;            

   void foo(Class X) throws Exception {  
      if(X!=A.class)
         throw new Exception("Incorrect parameter of "+this.getClass().getName()+".foo("+X.getName()+")");
      y++;
      System.out.printf("A.foo(%s): y=%d\n",X.getName(),y);
   }
   void foo() throws Exception { 
      System.out.printf("A.foo()\n");
      this.foo(this.getClass()); 
   }
}

class B extends A {     
   int y=20;            

   @Override
   void foo(Class X) throws Exception { 
      if(X==B.class) { 
         y++; 
         System.out.printf("B.foo(%s): y=%d\n",X.getName(),y);
      } else { 
         System.out.printf("B.foo(%s) calls B.super.foo(%s)\n",X.getName(),X.getName());
         super.foo(X);
      } 
   }
}

class C extends B {     
   int y=30;            

   @Override
   void foo(Class X) throws Exception { 
      if(X==C.class) { 
         y++; 
         System.out.printf("C.foo(%s): y=%d\n",X.getName(),y);
      } else { 
         System.out.printf("C.foo(%s) calls C.super.foo(%s)\n",X.getName(),X.getName());
         super.foo(X);
      } 
   }

   void DoIt() {
      try {
         System.out.printf("DoIt: foo():\n");
         foo();         
         Show();

         System.out.printf("DoIt: foo(B):\n");
         foo(B.class);  
         Show();

         System.out.printf("DoIt: foo(A):\n");
         foo(A.class);  
         Show();
      } catch(Exception e) {
         //...
      }
   }

   void Show() {
      System.out.printf("Show: A.y=%d, B.y=%d, C.y=%d\n\n", ((A)this).y, ((B)this).y, ((C)this).y);
   }
} 

objC.DoIt () 결과 출력 :

DoIt: foo():
A.foo()
C.foo(C): y=31
Show: A.y=10, B.y=20, C.y=31

DoIt: foo(B):
C.foo(B) calls C.super.foo(B)
B.foo(B): y=21
Show: A.y=10, B.y=21, C.y=31

DoIt: foo(A):
C.foo(A) calls C.super.foo(A)
B.foo(A) calls B.super.foo(A)
A.foo(A): y=11
Show: A.y=11, B.y=21, C.y=31

C #에서는 비가 상 메소드에서만 작동하며 모든 메소드는 Java에서 가상이기 때문에 실제로 다르지 않습니다.
Agent_L

0

간단하게 수행 할 수 있습니다. 예를 들어 :

B의 C 서브 클래스와 A의 B 서브 클래스. 둘 다 메소드 methodName ()이 있습니다.

public abstract class A {

    public void methodName() {
        System.out.println("Class A");
    }

}

public class B extends A {

    public void methodName() {
        super.methodName();
        System.out.println("Class B");
    }

    // Will call the super methodName
    public void hackSuper() {
        super.methodName();
    }

}

public class C extends B {

    public static void main(String[] args) {
        A a = new C();
        a.methodName();
    }

    @Override
    public void methodName() {
        /*super.methodName();*/
        hackSuper();
        System.out.println("Class C");
    }

}

클래스 C 실행 결과는 다음과 같습니다. 클래스 A 클래스 C

출력 대신 : 클래스 A 클래스 B 클래스 C


-1
public class SubSubClass extends SubClass {

    @Override
    public void print() {
        super.superPrint();
    }

    public static void main(String[] args) {
        new SubSubClass().print();
    }
}

class SuperClass {

    public void print() {
        System.out.println("Printed in the GrandDad");
    }
}

class SubClass extends SuperClass {

    public void superPrint() {
        super.print();
    }
}

출력 : GrandDad에서 인쇄


2
이 답변은 질문의 범위를 벗어납니다. OP는 조부모 클래스에서 메소드를 호출하는 방법을 묻지 않았습니다. 문제는 왜 super.super.method()Java에서 유효한 코드가 아닌지에 대한 토론입니다 .
Jed Schaaf

-1

키워드 super는 수퍼 클래스에서 메소드를 호출하는 방법입니다. Java 학습서에서 : https://docs.oracle.com/javase/tutorial/java/IandI/super.html

메소드가 수퍼 클래스의 메소드 중 하나를 대체하는 경우 키워드 super를 사용하여 대체 된 메소드를 호출 할 수 있습니다.

그것이 슈퍼 객체의 참조라고 믿지 마십시오! 아니요, 수퍼 클래스에서 메소드를 호출하는 키워드 일뿐입니다.

예를 들면 다음과 같습니다.

class Animal {
    public void doSth() {
        System.out.println(this);   // It's a Cat! Not an animal!
        System.out.println("Animal do sth.");
    }
}

class Cat extends Animal {
    public void doSth() {
        System.out.println(this);
        System.out.println("Cat do sth.");
        super.doSth();
    }
}

를 호출하면 클래스 cat.doSth()의 메소드 가 인쇄 되고 고양이입니다.doSth()Animalthis

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