我有一个带有公共insert()
方法的抽象类BaseClass:
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);
这可以在OOP中完成吗?
答案 0 :(得分:21)
隐藏方法无法实现。你可以这样做:
@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)
的类扩展基础对象的不同子类。 / p>
答案 1 :(得分:6)
我不相信有一种干净的方法可以完全隐藏Java中的继承方法。
在这种情况下,如果您绝对不能支持该方法,我可能会在子类中将该方法标记为@Obsolete,并让它抛出NotImplementedException(或Java中的等效异常),以不鼓励人们使用它。
最后,如果你继承了一个对你的子类没有意义的方法,那么你真的不应该继承那个基类。它也可能是基类设计不当或包含太多行为,但可能值得考虑您的类层次结构。另一个要查看的路径可能是组合,其中您的类具有以前作为基类的私有实例,您可以通过将它们包装在您自己的方法中来选择要公开的方法。 (编辑:如果基类是抽象的,组合可能不是一个选项...)
答案 2 :(得分:1)
正如Cletus指出的那样,这实际上是一个设计问题,因为您正在尝试创建一个不遵守其父类合同的子类。
在极少数情况下,通过以下方式解决此问题:抛出异常可能是可取的(或至少是一个可接受的折衷方案 - 例如,Java Collections Framework),但总的来说它是设计不佳的标志。
您可能希望阅读the Liskov substitution principle:(如维基百科所说)“如果S是T的子类型,那么程序中类型T的对象可能会被S类型的对象替换不改变该程序的任何理想属性“。通过覆盖抛出异常的方法或以任何其他方式隐藏它,你违反了这个原则。
如果基类'方法的契约是“插入当前对象,或者抛出异常”(参见例如the JavaDoc for Collection.add())那么你可能会认为你没有违反LSP,但如果这是意外的大多数来电者你可能想要在这些理由上重新考虑你的设计。
答案 3 :(得分:1)
这听起来像是一个设计糟糕的层次结构 -
如果不存在默认值且用户根本不应该调用该方法,则可以将该方法标记为@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) {
// ...
}
}