Есть ли способ сделать метод, который не является абстрактным, но должен быть переопределен?


есть ли способ заставить дочерние классы переопределить неабстрактный метод суперкласса?

Мне нужно иметь возможность создавать экземпляры родительского класса, но если класс расширяет этот класс, он должен дать свое собственное определение некоторых методов.

13 53

13 ответов:

насколько я знаю, нет прямого компилятор-принудительного способа сделать это.

вы могли бы обойти его не создание экземпляра родительского класса, но вместо этого предоставление метода фабрики, который создает экземпляр некоторого (возможного частного) подкласса, который имеет реализацию по умолчанию:

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

вы не могу написать new Base() сейчас, но вы можете сделать Base.create() чтобы получить реализацию по умолчанию.

как указывали другие, вы не можете сделать это напрямую.

но один из способов сделать это-использовать шаблон стратегии, например:

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

рассмотрите возможность создания интерфейса с помощью этого метода. Потомки класса должны будут его реализовать.

Я думаю, самый простой способ-создать абстрактный класс, который наследует от базового класса:


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

нет, в этом весь смысл абстрактного метода. Каков ваш вариант использования? Возможно, мы можем думать об этом, основываясь на базовой потребности.

Как насчет этого: внутри реализации метода по умолчанию используйте отражение, чтобы получить точный класс объекта. Если класс не соответствует вашему базовому классу точно, бросьте RuntimeException или эквивалент.

public class Parent {

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

}

здесь причина это невозможно!

производный класс может просто вызов реализации базового класса при переопределении метода.

так какой смысл заставлять классе переопределить метод? Я не думаю, что есть какая-то польза.

ответ будет "нет". Вы можете изменить дизайн, используя шаблон дизайна шаблона. Это может вам помочь.

кроме того, вы можете сделать ваши дочерние классы реализуют интерфейс. Интерфейс может или не может быть реализован суперклассом.

вы всегда можете сделать базовый класс имеет метод, который бросает исключение.

технически базовый класс определяет метод, но это не может использоваться без переопределения метода. В таком случае я предпочитаю исключение времени выполнения, поскольку они не требуют явного оператора throws. Ниже приведен пример

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

}

недостатком является то, что это не мешает созданию Base объекты; однако любой, кто пытается использовать один из методов " должен быть переопределен скоро обнаружится, что они должны были переопределить класс.

хотя это решение хорошо соответствует описанию запроса, ваше приложение, вероятно, выиграет от того, что не требует такого решения. Гораздо лучше избегать сбоев во время выполнения с проверками компилятора, что и обеспечивает ключевое слово abstract.

Я буду отражать другие ответы и скажу, что нет принудительного компилятора, чтобы заставить производные классы переопределить неабстрактный метод. Весь смысл создания абстрактного метода состоит в том, чтобы определить, что метод с этой сигнатурой должен существовать, но не может быть указан на базовом уровне и поэтому должен быть указан на производном уровне. Если есть рабочая, нетривиальная реализация (как в ней не пусто и не просто выбрасывает исключение или показывает сообщение) метода в базе уровне, то это не является строго необходимым для того, чтобы вызов метода от потребителя производного класса, чтобы добиться успеха. Таким образом, компилятору не нужно принудительно переопределять метод, который может успешно выполняться как на базовом, так и на производном уровнях.

в ситуациях, когда вы хотите, чтобы производный класс переопределял вашу рабочую реализацию, должно быть довольно очевидно, что базовая реализация не делает то, что хотят потребители производного класса; базовый класс либо не хватает реализации, либо неправильный. В этих случаях вы должны быть уверены, что программист, производный от вашего класса, будет знать, что он делает, и поэтому знает, что метод должен быть переопределен, потому что он не дает правильного ответа в контексте использования его нового объекта.

Я могу придумать одну вещь, которую вы могли бы сделать. Для этого потребуется абстрактная база с запечатанной (окончательной для Javaheads) реализацией "по умолчанию". Таким образом, основной реализация метода, который легко доступен для использования, как если бы это был" базовый " класс, но чтобы определить другой класс для нового сценария, вы должны вернуться к абстрактному классу и, таким образом, вынуждены переопределить метод. Этот метод может быть единственной абстрактной вещью в классе, что позволяет вам использовать базовые реализации других методов:

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

однако, это имеет два недостатка. Во-первых, потому что у вас нет доступа к DefaultClass реализация через наследование вы не можете расширить реализацию DefaultClass, что означает, что для выполнения того, что делает DefaultClass, плюс немного больше, вы должны переписать код из DefaultClass, нарушая DRY. Во-вторых, это работает только для одного уровня наследования, потому что вы не можете принудительно переопределить, если вы разрешаете наследование от DerivedClass.

может это поможет:

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

хорошо, давайте узнаем это таким образом. Я придерживаюсь принципов стиля java и использую синтаксис java. Поэтому предполагается, что шаблоны множественного наследования и C++ недоступны.

делать метод родительского класса абстрактным не обязательно, в ООП вы используете понятие полиморфизма. вы можете использовать один и тот же метод двумя или более различными способами. Это называется переопределением метода.

возьмем пример.

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

это будет печатать " meowww !!!"на экране.

но это не означает, что makeSound метод должен быть переопределен в дочернем классе.

Если вам нужно, чтобы дочерний класс был вынужден переопределить методы, вам лучше реализовать интерфейс классом.

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

это также будет печатать "meowwww !!!"на экране

Это может быть не рекомендуется, но вы можете бросить exeption( что-то вроде MethodeMustBeOverRiddenExp) в вашей реализации methode. Конечно, это форсирование времени выполнения, но может быть лучше, чем орехи.