Методы перегрузки и скрытия в Java
у меня есть абстрактный класс BaseClass с public 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 ответов
нет прячась метод. Вы можете сделать это:
@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)
расширить другой подкласс базового объекта.
Я не верю, что есть чистый способ, чтобы полностью скрыть унаследованный метод в Java.
в таких случаях, если вы абсолютно не можете поддержать этот метод, я бы, вероятно, пометил этот метод как @ устаревший в дочернем классе и заставил его бросить NotImplementedException (или любое эквивалентное исключение в Java), чтобы препятствовать людям использовать его.
в конце концов, если вы наследуете метод, который не имеет смысла для вашего дочернего класса, может быть, что вы не стоит наследовать от этого базового класса вообще. Возможно также, что базовый класс плохо спроектирован или охватывает слишком много поведения, но, возможно, стоит рассмотреть иерархию классов. Другим маршрутом может быть композиция, где ваш класс имеет частный экземпляр того, что раньше было базовым классом, и вы можете выбрать, какие методы предоставить, обернув их в свои собственные методы. (Edit: если базовый класс является абстрактным, композиция может не быть опцией...)
Как указывает Клетус, это действительно проблема дизайна, поскольку вы пытаетесь создать дочерний класс, который не подчиняется контракту своего родительского класса.
есть редкие обстоятельства, когда работа вокруг этого, например, бросая исключение, может быть желательной (или, по крайней мере, приемлемый компромисс-например, Java Collections Framework), но в целом это признак плохого дизайна.
вы можете прочитать на замена Лискова принцип: идея о том, что (как говорит Википедия) "если S является подтипом T, то объекты типа T в программе могут быть заменены объектами типа S без изменения каких-либо желательных свойств этой программы". Переопределяя метод для создания исключения или скрывая его любым другим способом, вы нарушаете этот принцип.
Если контракт метода базового класса был "вставляет текущий объект или создает исключение" (см., например,JavaDoc для Коллекция.add ()) тогда вы можете утверждать, что не нарушаете LSP, но если это неожиданно для большинства абонентов, вы можете пересмотреть свой дизайн на этих основаниях.
это звучит как плохо спроектированная иерархия -
если по умолчанию не существует, и пользователь не должен вызывать метод Вообще, вы можете пометить метод как @Deprecated
и бросить UnsupportedOperationException
как отмечали другие плакаты. - это действительно только проверка времени выполнения. @Deprecated
только выдает предупреждение компилятора, и большинство IDEs отмечают его каким-то образом, но это не предотвращает время компиляции. Это также действительно отстой, потому что можно получить дочерний класс в качестве родителя ссылка на класс и вызвать метод на нем без предупреждения, что это "плохо" вообще. В приведенном ниже примере до выполнения не будет никаких указаний на то, что что-то не так.
пример:
// 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) {
// ...
}
}