Como forçar a classe derivada a chamar o método super? (Como o Android faz)

85

Eu estava pensando, ao criar novas Activityaulas e, em seguida, substituindo o onCreate()método, no eclipse Eu sempre se auto acrescentou: super.onCreate(). Como isso acontece? Existe uma palavra-chave java na classe abstrata ou pai que força isso?

Não sei se é ilegal não chamar a superclasse, mas lembro em alguns métodos que recebi uma exceção lançada por não fazer isso. Isso também está integrado ao java? Você pode usar alguma palavra-chave para fazer isso? Ou como isso é feito?

Peterdk
fonte
Eu também quero saber disso. E não é apenas o Eclipse que está sendo útil, se eu remover a chamada para super.onCreate (), o aplicativo apresenta um erro de tempo de execução dizendo: "não chamou super.onCreate ()". Então, sim, está realmente forçando você a chamar o método super. Mas como?
Rodrigo Castro
@RodrigoCastro Você pode revisar os javadocs para cada método. Por exemplo, onCreate () .

Respostas:

10

Aqui está a fonte de Activity#onCreate()- são quase todos os comentários ( original - consulte a linha ~ 800 ):

/**
 * Called when the activity is starting.  This is where most initialization
 * should go: calling {@link #setContentView(int)} to inflate the
 * activity's UI, using {@link #findViewById} to programmatically interact
 * with widgets in the UI, calling
 * {@link #managedQuery(android.net.Uri , String[], String, String[], String)} to retrieve
 * cursors for data being displayed, etc.
 *
 * <p>You can call {@link #finish} from within this function, in
 * which case onDestroy() will be immediately called without any of the rest
 * of the activity lifecycle ({@link #onStart}, {@link #onResume},
 * {@link #onPause}, etc) executing.
 *
 * <p><em>Derived classes must call through to the super class's
 * implementation of this method.  If they do not, an exception will be
 * thrown.</em></p>
 *
 * @param savedInstanceState If the activity is being re-initialized after
 *     previously being shut down then this Bundle contains the data it most
 *     recently supplied in {@link #onSaveInstanceState}.  <b><i>Note: Otherwise it is null.</i></b>
 *
 * @see #onStart
 * @see #onSaveInstanceState
 * @see #onRestoreInstanceState
 * @see #onPostCreate
 */
protected void onCreate(Bundle savedInstanceState) {
    mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
            com.android.internal.R.styleable.Window_windowNoDisplay, false);
    mCalled = true;
}

então, meu palpite seria que o plug-in ADT Eclipse é o que adiciona automaticamente essa chamada super.onCreate()para você. É uma suposição total, no entanto.

Matt Ball
fonte
2
Acho que mCalled = truetambém é usado para uma possível exceção. Talvez não no, onCreate()mas quando de fato essa exceção for lançada, ele usará esse padrão simples.
Peterdk
194

Isso é adicionado à biblioteca de anotações de suporte:

dependencies {
    compile 'com.android.support:support-annotations:22.2.0'
}

http://tools.android.com/tech-docs/support-annotations

@CallSuper

runor49
fonte
Sim, você acertou em cheio aqui. Obrigado.
SMBiggs
Ainda não tentei, mas leia a documentação. Isso vai ser incrível, eu acho. Esta deve ser a resposta aceita.
Rizwan Sohaib de
1
Todas as outras respostas são totalmente uma porcaria, exceto esta. Deve ser aceito.
Le_Enot de
3
@RizwanSohaib ele não o forçará a fazer nada. Ele irá destacar e informar que você precisa ligar para ele. Para fazer algo mais complexo, você precisará de um processador de anotações ou de implementar a lógica você mesmo. dependendo do IDE, ele deve impedir você de construir também.
Frostymarvelous
1
Resposta perfeita. Muito obrigado
Võ Quang Hòa
82

Se você quiser forçar as subclasses a executar a lógica da classe pai, um padrão comum é algo como o seguinte:

public abstract class SuperClass implements SomeInterface
{
    // This is the implementation of the interface method
    // Note it's final so it can't be overridden
    public final Object onCreate()
    {
        // Hence any logic right here always gets run
        // INSERT LOGIC

        return doOnCreate();

        // If you wanted you could instead create a reference to the
        // object returned from the subclass, and then do some
        // post-processing logic here
    }

    protected abstract Object doOnCreate();
}

public class Concrete extends SuperClass
{
    @Override
    protected Object doOnCreate()
    {
        // Here's where the concrete class gets to actually do
        // its onCreate() logic, but it can't stop the parent
        // class' bit from running first

        return "Hi";
    }
}

Na verdade, isso não responde à sua pergunta sobre o que faz o Eclipse inserir automaticamente uma chamada de superclasse na implementação; mas não acho que seja o caminho a seguir, pois isso sempre pode ser excluído.

Você não pode realmente impor que um método deve chamar a versão da superclasse com uma palavra-chave Java ou algo parecido. Suspeito que suas exceções simplesmente vieram de algum código na classe pai verificando invariantes esperados, ou algo, que foram invalidados por sua abordagem. Observe que isso é sutilmente diferente de lançar uma exceção porque você falhou ao chamar super.onCreate().

Andrzej Doyle
fonte
8

Se você quiser ter certeza absoluta de que o método da superclasse também é chamado, você deve se enganar um pouco: não permita que o método da superclasse seja sobrescrito, mas faça com que ele chame um método protegido que pode ser substituído.

class Super
{
   public final void foo() {
      foo_stuff();
      impl_stuff();
   }

   protected void impl_stuff() {
      some_stuff_that_you_can_override();
   }
}

class Base extends Super
{
  protected void impl_stuff() { 
     my_own_idea_of_impl();
  }
}

Dessa forma, o usuário deve chamar Super.foo () ou Base.foo () e sempre será a versão da classe base como foi declarada como final. O material específico da implementação está em impl_stuff (), que pode ser sobrescrito.

Lagerbaer
fonte
8

Para responder à sua pergunta real, a autocriação da chamada para super.onCreate () é um recurso do plugin ADT. Em java, você não pode forçar diretamente uma subclasse a chamar a superimplementação de um método, afaik (consulte o padrão descrito em outras respostas para uma solução alternativa). No entanto, lembre-se de que, no Android, você não está instanciando objetos Activity (ou objetos Service) diretamente - você passa um Intent para o sistema e o sistema instancia o objeto e chama onCreate () sobre ele (junto com outros métodos de ciclo de vida). Portanto, o sistema tem uma referência de objeto direta para a instância Activity e é capaz de verificar (presumivelmente) algum Booleano definido como verdadeiro na implementação da superclasse de onCreate (). Embora eu não saiba exatamente como é implementado, provavelmente se parece com isto:

class Activity
{
  onCreate()
  {
    superCalled = true;
    ...
  }
  ...
}

E na classe de nível "sistema" que recebe o Intent e instancia o objeto Activity a partir dele:

...
SomeActivitySubclass someActivitySubclassObject = new SomeActivitySubclass();
someActivitySubclassObject.onCreate();
if (!someActivityObject.isSuperCalled())
{
  Exception e = new Exception(...) //create an exception with appropriate details
  throw e;
}

Meu palpite é que provavelmente é um pouco mais complexo do que isso, mas essa é a ideia. O Eclipse cria automaticamente a chamada porque o plug-in ADT o instrui, por conveniência. Boa codificação!

código de fala
fonte
4

Não há nada em Java que force a chamada de super e há muitos exemplos em que você não gostaria. O único lugar onde você pode forçar a chamada de super é nos construtores. Todos os construtores precisam chamar um construtor de superclasse. Um (o construtor sem argumentos) será inserido se você não escrever um explicitamente, e se não houver nenhum construtor sem argumentos, você deverá chamá-lo explicitamente.

DJClayworth
fonte
3

O Eclipse está apenas sendo útil, lembrando que você pode chamar a implementação da superclasse se desejar.

provavelmente você está obtendo um erro porque não está fazendo algo necessário que a superclasse faz, já que não está chamando sua implementação.

hvgotcodes
fonte