Pergunta

Existe alguma maneira de forçar as classes filhas a substituir um método não abstrato de superclasse?

Eu preciso ser capaz de criar instâncias da classe pai, mas se uma classe estende essa classe, ela deve fornecer sua própria definição de alguns métodos.

Foi útil?

Solução

Não há uma maneira direta imposta pelo compilador de fazer isso, até onde eu sei.

Você poderia contornar isso não tornando a classe pai instanciável, mas fornecendo um método de fábrica que cria uma instância de alguma subclasse (possivelmente privada) que tem a implementação padrão:

public abstract class Base {
  public static Base create() {
    return new DefaultBase();
  }

  public abstract void frobnicate();

  static class DefaultBase extends Base {
    public void frobnicate() {
      // default frobnication implementation
    }
  }
}

Você não pode escrever new Base() agora, mas pode fazer Base.create() para obter a implementação padrão.

Outras dicas

Como outros observaram, você não pode fazer isso diretamente.

Mas uma maneira de fazer isso é usar o padrão de estratégia , assim:

public class Base {
    private final Strategy impl;

    // Public factory method uses DefaultStrategy
    // You could also use a public constructor here, but then subclasses would
    // be able to use that public constructor instead of the protected one
    public static Base newInstance() {
        return new Base(new DefaultStrategy());
    }

    // Subclasses must provide a Strategy implementation
    protected Base(Strategy impl) {
        this.impl = impl;
    }

    // Method is final: subclasses can "override" by providing a different
    // implementation of the Strategy interface
    public final void foo() {
        impl.foo();
    }

    // A subclass must provide an object that implements this interface
    public interface Strategy {
        void foo();
    }

    // This implementation is private, so subclasses cannot access it
    // It could also be made protected if you prefer
    private static DefaultStrategy implements Strategy {
        @Override
        public void foo() {
            // Default foo() implementation goes here
        }
    }
}

Considere fazer uma interface com este método.Descendentes de classe terão que implementá-lo.

Acho que a maneira mais fácil é criar uma classe abstrata que herda da classe base:


public class Base {
    public void foo() {
        // original method
    }
}

abstract class BaseToInheritFrom extends Base {
    @Override
    public abstract void foo();
}

class RealBaseImpl extends BaseToInheritFrom {
    @Override
    public void foo() {
        // real impl
    }
}

Não, esse é o ponto principal de um método abstrato.Qual é o seu caso de uso?Talvez possamos pensar sobre isso com base na necessidade subjacente.

Que tal isso: dentro da implementação padrão do método, use reflexão para obter a classe exata do objeto.Se a classe não corresponder exatamente à sua classe base, lance uma RuntimeException ou equivalente.

public class Parent {

    public void defaultImpl(){
        if(this.getClass() != Parent.class){
            throw new RuntimeException();
        }
    }

}

Há um motivo para que não seja possível!

A classe derivada pode simplesmente chamar a implementação da classe base ao substituir o método.

Então, por que forçar a classe a sobrescrever seu método?Não acho que haja nenhum benefício.

A resposta seria não.Você pode redesenhar usando o padrão de design de modelo.Isso pode te ajudar.

Como alternativa, você pode fazer com que suas classes filhas implementem uma interface.A interface pode ou não ser implementada pela superclasse.

Você sempre pode fazer com que a classe base tenha um método que lance uma exceção.

Tecnicamente, a classe base definiu o método, mas não pode ser usado sem substituir o método.Nesse caso, prefiro uma exceção de tempo de execução, pois não exigem uma instrução throws explícita.Segue um exemplo

public class Parent {

  public void doStuff() {
    throw new RuntimeException("doStuff() must be overridden");
  }

}

public class Child extends Parent {

  @Override
  public void doStuff() {
    ... all is well here ...
  }

}

A desvantagem é que isso não impede a criação de objetos Base;no entanto, qualquer pessoa que tentar usar um dos métodos "deve ser substituído" logo descobrirá que deveria ter substituído a classe.

Embora esta solução atenda bem à descrição da solicitação, seu aplicativo provavelmente se beneficiaria por não exigir uma solução como esta.É muito melhor evitar travamentos de tempo de execução com verificações do compilador, que é o que a palavra-chave abstrata fornece.

Vou espelhar as outras respostas e dizer que não há uma maneira imposta pelo compilador de forçar as classes derivadas a substituir um método não abstrato. O objetivo de tornar um método abstrato é definir que um método com essa assinatura deve existir, mas não pode ser especificado no nível de base e, portanto, deve ser especificado em um nível derivado. Se houver uma implementação não trivial funcional (como se ela não estivesse vazia e não apenas lançasse uma exceção ou mostrasse uma mensagem) de um método no nível de base, isso não seria estritamente necessário para uma chamada o método de um consumidor da classe derivada seja bem-sucedido. Portanto, o compilador não precisa impor a substituição de um método que pode ser executado com bastante êxito nos níveis básicos ou derivados.

Em situações em que você deseja que uma classe derivada substitua sua implementação de trabalho, deve ser bastante óbvio que a implementação de base não faz o que os consumidores da classe derivada desejam; a classe base não tem implementação suficiente ou a classe errada. Nesses casos, você deve confiar que um programador derivado de sua classe saberá o que está fazendo e, portanto, saberá que o método precisa ser substituído porque não produz a resposta certa no contexto de uso de seu novo objeto.

Posso pensar em uma coisa que você poderia fazer. Isso exigiria uma base abstrata, com uma implementação "padrão" selada (final para os Javaheads). Dessa forma, há uma implementação básica do método que está prontamente disponível para uso como se fosse uma classe "base", mas para definir uma classe diferente para um novo cenário, você deve voltar para a classe abstrata, e são, portanto, forçados a reimplementar o método. Este método pode ser a única coisa abstrata na classe, ainda permitindo que você faça uso de implementações básicas de outros métodos:

public abstract class BaseClass
{
   public abstract void MethodYouMustAlwaysOverride();

   public virtual void MethodWithBasicImplementation() { ... }
}

public final class DefaultClass:BaseClass
{
   public override void MethodYouMustAlwaysOverride() { ... }

   //the base implementation of MethodWithBasicImplementation 
   //doesn't have to be overridden
}

...

public class DerivedClass:BaseClass
{
   //Because DefaultClass is final, we must go back to BaseClass,
   //which means we must reimplement the abstract method
   public override void MethodYouMustAlwaysOverride() { ... }

   //again, we can still use MethodWithBasicImplementation, 
   //or we can extend/override it
   public override void MethodWithBasicImplementation() { ... }
}

No entanto, isso tem duas desvantagens. Primeiro, porque você não tem acesso à implementação de DefaultClass por herança, você não pode estender a implementação de DefaultClass, o que significa que para fazer o que DefaultClass faz, mais um pouco mais, você deve reescrever o código de DefaultClass, violando DRY. Em segundo lugar, isso só funciona para um nível de herança, porque você não pode forçar a substituição se permitir a herança de DerivedClass.

talvez isso ajude:

class SuperClass {

    void doStuff(){

        if(!this.getClass().equals(SuperClass.class)){

            throw new RuntimeException("Child class must implement doStuff Method");
        }else{
            //ok
            //default implementation
        }
    }
}

class Child extends SuperClass{

    @Override
    void doStuff() {
        //ok
    }
}

class Child2 extends SuperClass{

}


 new SuperClass().doStuff(); //ok
 new Child().doStuff();         //ok
 new Child2().doStuff();        //error

ok, vamos aprender assim.Eu sigo as diretrizes de estilo java e uso a sintaxe java.Portanto, presume-se que a herança múltipla e os modelos C ++ não estão disponíveis.

tornar o método da classe pai abstrato não é obrigatório, em OOP você usa o conceito de polimorfismo.você pode usar o mesmo método de duas ou mais maneiras diferentes. Isso é chamado de substituição de método.

vamos dar um exemplo.

    public class Animal{
       public void makeSound(){
          System.out.println("Animal doesn't know how to make sound");
       }

    }

    public class PussyCat extends Animal{
        public void makeSound(){

           System.out.println("meowwww !!!");
        }

        public static void main(String args[]){
           PussyCat aCat=new PussyCat();
           aCat.makeSound(); 
        }
    }

isso imprimirá "meowww !!!"na tela.

mas isso não implica que o método makeSound deva ser substituído na classe filha.

Se você precisa que a classe filha seja forçada a substituir os métodos, é melhor implementar uma interface pela classe.

      public Interface audible{
         public void makeSound();

      }

      public class pussyCat implements audible{
          // now you must implement the body of makeSound method here
          public void makeSound(){
            System.out.println("meowwww !!!"); 
          }

          public static void main(String args[]){
           PussyCat aCat=new PussyCat();
           aCat.makeSound(); 
        }
      }

isso também imprimirá "meowwww !!!"na tela

Pode não ser recomendado, mas você pode lançar uma exceção (algo como MethodeMustBeOverRiddenExp) em sua implementação de método. Claro que é forçante do tempo de execução, mas pode ser melhor do que engessar.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top