Перегрузка и скрытие методов в Java - PullRequest
14 голосов
/ 07 апреля 2010

У меня есть абстрактный класс 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);

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

Ответы [ 4 ]

21 голосов
/ 07 апреля 2010

Нет способа скрыть метод. Вы можете сделать это:

@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), расширить другой подкласс базового объекта.

6 голосов
/ 07 апреля 2010

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

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

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

1 голос
/ 07 апреля 2010

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

Если по умолчанию не существует, и пользователь вообще не должен вызывать метод, вы можете пометить метод как @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) {
        // ...
    }
}
1 голос
/ 07 апреля 2010

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

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

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

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

...