Por que super.super.method (); não permitido em Java?


360

Eu li esta pergunta e pensei que seria facilmente resolvido (não que não seja solucionável sem) se alguém pudesse escrever:

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

Não tenho certeza se é útil em muitos casos, mas me pergunto por que não é e se existe algo assim em outros idiomas.

O que é que vocês acham?

Edição: Para esclarecer: sim, eu sei, isso é impossível em Java e eu realmente não sinto falta. Isso não era nada que eu esperava que funcionasse e fiquei surpreso ao receber um erro do compilador. Eu apenas tive a ideia e gostaria de discuti-la.


7
O desejo de telefonar super.super.toString()contradiz sua própria decisão quando você escolhe estender uma classe, aceitando todos (e não alguns) seus recursos.
DayaMoon

Respostas:


484

Viola o encapsulamento. Você não deve ignorar o comportamento da classe pai. Às vezes, faz sentido ignorar o comportamento de sua própria classe (principalmente de dentro do mesmo método), mas não o de seus pais. Por exemplo, suponha que tenhamos uma "coleção de itens" base, uma subclasse representando "uma coleção de itens vermelhos" e uma subclasse que representa "uma coleção de itens vermelhos grandes". Faz sentido ter:

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);
    }
}

Tudo bem - o RedItems sempre pode ter certeza de que os itens que ele contém estão todos vermelhos. Agora suponha que fosse capaz de chamar 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);
    }
}

Agora podemos adicionar o que quisermos, e o invariante em RedItems é quebrada.

Isso faz sentido?


38
bom exemplo. mas achei que era um design ruim quando a classe base aceita itens, mas a classe derivada os rejeita, porque a derivada não pode ser usada como um substituto para a classe base (violação do princípio da substituição). esse pensamento está correto ou essa hierarquia está limpa?
Johannes Schaub - litb 25/02/09

5
Você quer dizer composição sobre herança? Este exemplo não é um motivo para evitar a herança - embora existam muitos outros.
23909 Jon Skeet

3
@ Tim: Este foi apenas um exemplo fácil de entender de violar o encapsulamento. Poderia estar definindo uma propriedade. Além disso, nem todos os aspectos serão visíveis no nível do tipo. Genéricos não é a resposta para tudo.
23909 Jon Skeet

12
@ JohannesSchaub-litb Acho que viola o princípio de substituição de Liskov, pois se alguém codifica o contrato do Item e usa uma instância de RedItems, obtém NotRedItemException inesperado. Sempre fui ensinado que uma subclasse deve receber um superconjunto de entradas e retornar um subconjunto de saídas. Em outras palavras, uma subclasse nunca deve rejeitar uma entrada que seja válida para a superclasse ou produzir saída que a superclasse não possa produzir, isso inclui gerar um erro que a superclasse não lança.
Konstantin Tarashchanskiy 18/01/12

4
@piechuckerr: Às vezes livros, às vezes no blog, por vezes, apenas experiência ...
Jon Skeet

72

Eu acho que Jon Skeet tem a resposta correta. Gostaria de acrescentar que você pode acessar variáveis ​​sombreadas de superclasses de superclasses lançando 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();
        }
}

que produz a saída:

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

(exemplo do JLS )

No entanto, isso não funciona para chamadas de método, porque elas são determinadas com base no tipo de tempo de execução do objeto.


6
Se você tiver uma variável com o mesmo nome de uma superclasse e, por algum motivo, não puder (ou não tiver permissão para) alterá-la, ainda poderá acessar a variável da superclasse com o mesmo nome. Qual é a utilidade, você pergunta? Bem ... eu nunca usei.
Michael Myers

Ótimo link para o documento de expressões. Alguns bons exemplos para as pessoas que estudam na OCPJP.
18711 Gordon

Impressionante. Eu nunca teria pensado em usar um elenco.
Thomas Eding

como a classe T1 substitui a variável x? sua final estática por padrão, certo?
amarnath harish

@amarnathharish: não é substituído, e os campos são protegidos por pacote não finais por padrão.
Michael Myers

41

Eu acho que o código a seguir permite usar super.super ... super.method () na maioria dos casos. (mesmo que seja feio fazer isso)

Em resumo

  1. criar instância temporária do tipo ancestral
  2. copiar valores de campos do objeto original para um temporário
  3. chamar o método de destino no objeto temporário
  4. copiar valores modificados de volta ao objeto original

Uso:

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
Com a reflexão, você pode fazer qualquer coisa, sim :) Você pode até tornar as strings mutáveis.
BalusC

23
Que coisa horrível de se fazer! Eu estou te dando uma apenas para descobrir como fazê-lo em todos os :)
Larry Watanabe

6
É um bom truque, mas mesmo que isso nem sempre seja equivalente a chamar de super.super, o inevitável, mas necessário), isso ocorre porque a chamada super.super carregaria o contexto de C (C + B + A), enquanto suas respostas criam uma instância de A sem o contexto de B e C. Portanto, esta resposta não funcionará se todos os doThat chamados getContext (), por exemplo, e getContext forem implementados de maneira diferente em cada classe. na sua resposta, usaria o getContext () de A, enquanto chamar o super.super indisponível resultaria no uso do getContext de C.
INOR

Hmm. Em alguns casos, talvez você possa superar a objeção do inor com proxies dinâmicos ( javahowto.blogspot.co.uk/2011/12/… ), redirecionando as chamadas de método para o objeto original (sincronizando variáveis ​​após cada chamada)? Parece que os proxies exigem que tudo esteja implementando uma interface. Além disso, gostaria de saber se é possível para a classe super-super para chamar um de seus métodos de super-super, especificamente, e você não precisa redirecionar os ....
Erhannis

Eu disse em outro comentário que o bloqueio super.super.convida os programadores a encontrar maneiras novas, complicadas e flagrantes de dar um tiro no pé em busca de uma solução alternativa, este é um exemplo perfeito disso, porque seus colegas provavelmente o odeiam tanto por escrever algo assim eles pessoalmente e literalmente lhe darão um tiro no pé. +1
Braden Best

11

Como não tenho reputação suficiente para comentar, adicionarei isso às outras respostas.

Jon Skeet responde excelentemente, com um belo exemplo. Matt B tem razão: nem todas as superclasses têm supers. Seu código seria quebrado se você chamasse um super de um super que não tivesse super.

A programação orientada a objetos (que é Java) é sobre objetos, não funções. Se você deseja programação orientada a tarefas, escolha C ++ ou outra coisa. Se o seu objeto não se encaixa na sua superclasse, é necessário adicioná-lo à "classe dos avós", criar uma nova classe ou encontrar outro super em que ele se encaixe.

Pessoalmente, achei essa limitação um dos maiores pontos fortes de Java. O código é um pouco rígido em comparação com outros idiomas que eu usei, mas eu sempre sei o que esperar. Isso ajuda com o objetivo "simples e familiar" de Java. Em minha opinião, chamar super.super não é simples ou familiar. Talvez os desenvolvedores sentissem o mesmo?


3
Você diz "nem todas as superclasses têm supers". Bem, tudo, menos java.lang.Object, o que poderia lhe dar "nulo". Então, eu diria que quase todos têm supers.
Tim Büthe 25/02/09

Cada classe o programador da aplicação escreve tem um "super" (java.lang.Object não, mas o programador da aplicação não escreve isso.)
finnw

2
Facilmente resolvido, tornando super.super.super ... super um erro de tempo de compilação, se houver muitas substituições. Considerando que Java possui apenas herança pública, se alguém alterou a hierarquia de herança, você está alterando a interface. Portanto, eu não ficaria preocupado com o fato de o super ^ n ser assustador.
Thomas Eding

7

Existem algumas boas razões para fazer isso. Você pode ter uma subclasse que possui um método implementado incorretamente, mas o método pai é implementado corretamente. Por pertencer a uma biblioteca de terceiros, talvez você não consiga / não queira alterar a fonte. Nesse caso, você deseja criar uma subclasse, mas substitui um método para chamar o método super.super.

Conforme mostrado em alguns outros pôsteres, é possível fazer isso através da reflexão, mas deve ser possível fazer algo como

(SuperSuperClasse isso) .theMethod ();

Estou lidando com esse problema agora - a solução rápida é copiar e colar o método da superclasse no método da sub-classe :)


11
A transmissão antes de chamar um método não altera o método ao qual está delegado - sempre é a implementação da subclasse usada, nunca a super. Veja, por exemplo, stackoverflow.com/questions/1677993/…
Joshua Goldberg

11
@ Larry, esta é exatamente a situação em que eu estava, e exatamente a correção que usei. Boa decisão!
BCR

Se você tiver o código do método necessário, poderá abrir todos os campos necessários e executar esse código na sua subclasse.
usar o seguinte comando

6

Além dos pontos muito bons que outros fizeram, acho que há outro motivo: e se a superclasse não tiver uma superclasse?

Como toda classe se estende naturalmente (pelo menos) Object, super.whatever()sempre se refere a um método na superclasse. Mas e se a sua aula se estender apenas Object- a que se super.superreferiria então? Como esse comportamento deve ser tratado - um erro do compilador, um NullPointer, etc?

Eu acho que a principal razão pela qual isso não é permitido é que viola o encapsulamento, mas isso também pode ser uma pequena razão.


4
Obviamente, seria um erro do compilador - e são as informações que o compilador possui.
22711 Michael Borgwardt

4

Eu acho que se você sobrescrever um método e quiser toda a versão de equalssuperclasse (como, digamos, por ), então você praticamente sempre chamará a versão direta da superclasse primeiro, que chamará sua versão da superclasse por sua vez, se quiser .

Eu acho que raramente faz sentido (se é que não consigo pensar em um caso em que isso ocorre) chamar a versão de um método de alguma superclasse arbitrária. Não sei se isso é possível em Java. Isso pode ser feito em C ++:

this->ReallyTheBase::foo();

6
Faz sentido se alguém escreveu uma subclasse com um método implementado incorretamente, mas o método da superclasse faz 90% do trabalho. Então você deseja criar uma subclasse e substituir o método que chama o método de superclasse da superclasse e adicionar seus próprios 10%.
Larry Watanabe

3

Em suposição, porque não é usado com tanta frequência. A única razão pela qual pude ver usá-lo é se seu pai direto substituiu alguma funcionalidade e você está tentando restaurá-lo de volta ao original.

O que me parece contrário aos princípios da OO, uma vez que os pais diretos da classe devem estar mais intimamente relacionados à sua classe do que o avô.


3

Veja este projeto do Github, especialmente a variável objectHandle. Este projeto mostra como realmente e com precisão chamar o método de avô em um neto.

Apenas no caso de o link ser quebrado, aqui está o código:

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";
        }
    }
}

Feliz codificação !!!!


Seus cientistas estavam tão preocupados se poderiam ou não, que não pararam para pensar se deveriam. Por favor, não realmente fazer isso ...: P 🤔
Tim Buthe

Sim, essas são as palavras do programador, não as minhas. Na minha opinião, eu acho que você pode realmente precisar dele algum dia
kyay

2

Eu colocaria o corpo do método super.super em outro método, se possível

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();
    }
}

Ou, se você não pode alterar a classe super-super, pode tentar o seguinte:

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;
    }
}

Nos dois casos, o

new ChildClass().toString();

resultados para "eu sou super super"


Acabei de me encontrar em uma situação em que possuo o SuperSuperClass e o ChildClass, mas não o SuperClass, então achei a primeira solução útil.
xofon 15/01/16


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);

     }
}

execução: UM CONSTRUÇÃO BEM SUCEDIDA (tempo total: 0 segundos)


11
Entendo, mas você está criando uma nova instância para que isso não funcione se o objeto tiver algum estado.
Tim Büthe

1

A chamada de super.super.method () faz sentido quando você não pode alterar o código da classe base. Isso geralmente acontece quando você está estendendo uma biblioteca existente.

Pergunte a si mesmo primeiro, por que você está estendendo essa aula? Se a resposta for "porque não posso alterá-la", você poderá criar pacote e classe exatos no seu aplicativo e reescrever o método impertinente ou criar delegado:

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() {...}
    ...
}

Por exemplo, você pode criar a classe org.springframework.test.context.junit4.SpringJUnit4ClassRunner em seu aplicativo, para que essa classe seja carregada antes da real do jar. Em seguida, reescreva métodos ou construtores.

Atenção: Este é um hack absoluto, e não é altamente recomendável usar, mas está FUNCIONANDO! O uso dessa abordagem é perigoso devido a possíveis problemas com os carregadores de classes. Além disso, isso pode causar problemas sempre que você atualizar a biblioteca que contém a classe substituída.


1

Eu tive situações como estas quando a arquitetura é criar funcionalidades comuns em um CustomBaseClass comum que implementa em nome de várias classes derivadas. No entanto, precisamos contornar a lógica comum para um método específico para uma classe derivada específica. Nesses casos, devemos usar uma implementação super.super.methodX.

Conseguimos isso introduzindo um membro booleano no CustomBaseClass, que pode ser usado para adiar seletivamente a implementação customizada e render a implementação da estrutura padrão, sempre que desejável.

        ...
        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(...);
                       }
        }

No entanto, com bons princípios de arquitetura seguidos na estrutura e no aplicativo, poderíamos evitar essas situações facilmente, usando a abordagem hasA, em vez da abordagem isA. Mas, a todo momento, não é muito prático esperar uma arquitetura bem projetada e, portanto, a necessidade de se afastar de princípios sólidos de design e introduzir hacks como esse. Apenas meus 2 centavos ...


1

@ Jon Skeet Boa explicação. IMO, se alguém quiser chamar o método super.super, deve ignorar o comportamento do pai imediato, mas deseja acessar o comportamento do pai principal. Isso pode ser alcançado através da instância Of. Como código abaixo

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();
    }
}

Aqui está a classe do driver,

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

A saída disso será

In C Class
In A Class

O comportamento da classe B printClass será ignorado nesse caso. Não tenho certeza sobre isso é uma prática ideal ou boa para obter super.super, mas ainda está funcionando.


11
Bem, isso é criativo, mas realmente não responde à minha pergunta. C ainda não chama super.super, B apenas se comporta de maneira diferente. Se você pode alterar A e B, basta adicionar outro método em vez de usar instanceof. Super.super.foo o ajudaria nos casos em que você não tem acesso a A e B e não poderia alterá-los.
Tim Büthe

Concorde com @TimButhe, mas se alguém quiser chamar super.super, ele / ela intencionalmente deseja ignorar o comportamento da classe pai, então você só precisa conseguir isso com a sintaxe existente do java. (Qualquer uma opção desejada método seja instanceof / diferente)
Sanjay Jain

0

Se você acha que vai precisar da superclasse, pode referenciá-la em uma variável para essa classe. Por exemplo:

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;
  }
}

Deve imprimir:

2
1
0

2
Seu exemplo é meio que ... muito ruim, porque você usa métodos estáticos. Ao usar métodos estáticos, você não precisa da variável ou super. Talvez você tenha perdido algum conceito básico de OO aqui, certifique-se de ler sobre isso. Tem que votar, desculpe.
Tim Büthe 6/06/11

11
Poderia ter sido feito facilmente sem métodos estáticos. É bastante simples. Eu estava indo para um exemplo simples de como fazer isso.
Ashtheking

Entendo que armazenar a super variável em um campo é uma maneira de resolver isso. Mas, você não precisa da variável, se for um método estático, você pode apenas usá-lo. Em segundo lugar, invocar métodos estáticos e uma variável é uma prática ruim e a maioria dos IDEs o alertará sobre isso. Se você resolver sua resposta, removendo o material estático, ficarei feliz em remover meu voto negativo, sem ofensa.
Tim Büthe

Você está perto, mas seu código não será compilado. Você tenta acessar getNumber a partir de um contexto estático no seu método principal. Você realmente tentou compilar isso? (e should'nt seu UltraFoo estender SuperFoo?)
Tim Buthe

Eu realmente não quero ser cruel com você, mas new UltraFoo.getNumber()não vou compilar, pois você perdeu parênteses lá. No entanto, acabei de remover meu donvote, pois o conceito do seu código está bem claro agora, obrigado!
Tim Büthe

0

IMO, é uma maneira limpa de obter super.super.sayYourName()comportamento em 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);
    }
}

Resultado:

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


ah, então você está defendendo a implementação de uma hierarquia de classes como essa? Infelizmente, isso começa a ficar muito confuso se você quiser acessar o método em Mama do bebê (subclasse da filha) ...
BCR

Yakov Fain está certo. em outras palavras, não é um bom exemplo, porque a pergunta original era sobre chamar um método substituído no super.super.
INOR

0

Eu acho que esse é um problema que quebra o contrato de herança.
Ao estender uma aula, você obedece / concorda com seu comportamento,
enquanto Embora ao ligar super.super.method(), deseja quebrar seu próprio acordo de obediência.

Você simplesmente não pode escolher a super classe .

No entanto, pode ocorrer situações em que você sinta necessidade de ligar super.super.method()- geralmente um sinal de design ruim, no seu código ou no código que você herdou!
Se as super e super super classes não puderem ser refatoradas (algum código legado), opte pela composição em vez da herança.

Quebra de encapsulamento é quando você @ Substitui alguns métodos quebrando o código encapsulado. Os métodos projetados para não serem substituídos são marcados como finais .


0

Em C #, você pode chamar um método de qualquer ancestral como este:

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();
    }

Você também pode fazer isso no Delphi:

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

Mas em Java você pode fazer esse foco apenas de alguma forma. Uma maneira possível é:

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);
   }
} 

saída do resultado 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

No C #, ele funcionará apenas para métodos não virtuais e, como todos os métodos são virtuais em Java, não é realmente diferente.
Agent_L

0

É simplesmente fácil de fazer. Por exemplo:

Subclasse C da subclasse B e B de A. Os três têm o método methodName () por exemplo.

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");
    }

}

Executar classe C A saída será: Classe A Classe C

Em vez de saída: Classe A Classe B Classe 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();
    }
}

Saída: Impresso no GrandDad


2
Esta resposta está fora do escopo da pergunta. O OP não perguntou como chamar um método em uma classe de avós. A questão é uma discussão sobre por que o super.super.method()código não é válido em Java.
Jed Schaaf

-1

A palavra-chave super é apenas uma maneira de chamar o método na superclasse. No tutorial de Java: https://docs.oracle.com/javase/tutorial/java/IandI/super.html

Se o seu método substituir um dos métodos da superclasse, você poderá invocar o método substituído usando a palavra-chave super.

Não acredite que é uma referência do super objeto !!! Não, é apenas uma palavra-chave para invocar métodos na superclasse.

Aqui está um exemplo:

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();
    }
}

Quando você liga cat.doSth(), o método doSth()em classe Animalé impresso thise é um gato.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.