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

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

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

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

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

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



답변

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

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 은 깨졌습니다.

말이 돼?


답변

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의 예 )

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


답변

다음 코드는 대부분의 경우 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);
    }
}


답변

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

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

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

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


답변

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

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

(SuperSuperClass this) .theMethod ();

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


답변

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

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

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


답변

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

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

this->ReallyTheBase::foo();