Есть ли способ сделать метод, который не является абстрактным, но должен быть переопределен? - PullRequest
53 голосов
/ 20 октября 2011

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

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

Ответы [ 13 ]

37 голосов
/ 20 октября 2011

Насколько мне известно, не существует прямого способа реализации этого с помощью компилятора.

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

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() чтобы получить реализацию по умолчанию.

24 голосов
/ 21 октября 2011

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

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

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
        }
    }
}
6 голосов
/ 20 октября 2011

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

5 голосов
/ 04 февраля 2012

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


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
    }
}
3 голосов
/ 20 октября 2011

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

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

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

3 голосов
/ 20 октября 2011

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

public class Parent {

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

}
3 голосов
/ 20 октября 2011

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

2 голосов
/ 20 октября 2011

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

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

1 голос
/ 20 октября 2011

может быть, это поможет:

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
1 голос
/ 20 октября 2011

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

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

Я могу вспомнить одну вещь, которую вы могли бы сделать.Это потребовало бы абстрактной базы с запечатанной (окончательной для 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...