Подтвердить что ты не робот

Методы перегрузки и скрытия в Java

У меня есть абстрактный класс BaseClass с общедоступным методом insert():

public abstract class BaseClass {

 public void insert(Object object) {
  // Do something
 }

}

который расширяется многими другими классами. Однако для некоторых из этих классов метод insert() должен иметь дополнительные параметры, поэтому вместо их переопределения я перегружаю метод базового класса требуемыми параметрами, например:

public class SampleClass extends BaseClass {

 public void insert(Object object, Long param){
  // Do Something
 }

}

Теперь, если я создаю экземпляр класса SampleClass, у меня есть два метода insert():

SampleClass sampleClass = new SampleClass();
sampleClass.insert(Object object);
sampleClass.insert(Object object, Long param);

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

SampleClass sampleClass = new SampleClass();
sampleClass.insert(Object object, Long param);

Может ли это быть сделано в ООП?

4b9b3361

Ответ 1

Невозможно скрыть метод. Вы можете сделать это:

@Override
public void insert(Object ob) {
  throw new UnsupportedOperationException("not supported");
}

но что он.

Базовый класс создает контракт. Все подклассы связаны этим контрактом. Подумайте об этом так:

BaseObject b = new SomeObjectWithoutInsert();
b.insert(...);

Как этот код должен знать, что у него нет метода insert(Object)? Он не может.

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

Ответ 2

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

В таких случаях, если вы абсолютно не можете поддерживать этот метод, я бы, вероятно, пометил этот метод как @Obsolete в дочернем классе и попросил его исключить NotImplementedException (или что бы то ни было эквивалентное исключение в Java), чтобы препятствовать людям использовать его.

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

Ответ 3

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

Существуют редкие обстоятельства, при которых работа вокруг этого, например, может быть желательно нежелательное исключение (или, по крайней мере, приемлемый компромисс - например, Java Collections Framework), но в целом это признак плохого дизайна.

Вы можете прочитать принцип подстановки Лискова: идея, что (как это делает Википедия) ", если S является подтипом из T, тогда объекты типа T в программе могут быть заменены объектами типа S без изменения каких-либо желательных свойств этой программы". Переопределяя метод для исключения или скрывая его каким-либо другим способом, вы нарушаете этот принцип.

Если контракт метода базового класса был "вставляет текущий объект или генерирует исключение" (см., например, JavaDoc для Collection.add( )), тогда вы можете утверждать, что не нарушаете LSP, но если это неожиданно для большинства абонентов, вы можете захотеть переосмыслить свой дизайн на этих основаниях.

Ответ 4

Это звучит как плохо спроектированная иерархия -

Если по умолчанию не существует, и пользователь не должен вызывать метод вообще, вы можете пометить метод как @Deprecated и выбросить UnsupportedOperationException, как отметили другие плакаты. Однако - это действительно только проверка времени выполнения. @Deprecated только выдает предупреждение о компиляторе, и большинство IDE его каким-то образом отмечают, но нет возможности избежать компиляции. Это также действительно отстой, потому что можно получить дочерний класс как ссылку на родительский класс и вызвать метод на нем без предупреждения о том, что он "плохой" вообще. В приведенном ниже примере не будет никаких указаний до тех пор, пока не будет выполнено что-то неправильное.

Пример:

// Abstract base builder class
public abstract class BaseClassBuilder {
    public final doBuild() {
        BaseClass base = getBase();
        for (Object obj : getObjects() {
            base.insert(obj);
        }
    }
    protected abstract BaseClass getBase();
    protected abstract Object[] getObjects();
}

// implementation using SampleClass
public class SampleClassBuilder extends BaseClassBuilder {

    @Override
    protected BaseClass getBase() {
        return new SampleClass();
    }

    @Override
    protected Object[] getObjects() {
        Object[] obj = new Object[12];
        // ...
        return obj;
    }
}

Однако, если разумное значение по умолчанию существует, вы можете пометить унаследованный метод как final и указать значение по умолчанию внутри него. Это касается как плохой иерархии, так и предотвращает "непредвиденные обстоятельства" приведенного выше примера.

Пример:

public abstract class BaseClass { 
    public void insert(Object object) {
        // ...
    }
}

public class SampleClass extends BaseClass {

    public static final Long DEFAULT_PARAM = 0L;

    public final void insert(Object object) {
        this.insert(object, DEFAULT_PARAM);
    }

    public void insert(Object object, Long param) {
        // ...
    }
}