Java中的重载和隐藏方法

我有一个带有public 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中完成吗?

无法隐藏该方法。 你可以这样做:

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

但就是这样。

基类创建契约。 所有子类都受该合同约束。 这样考虑一下:

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

该代码如何知道它没有insert(Object)方法? 它不能。

你的问题听起来像是一个设计问题。 有问题的类不应该从有问题的基类inheritance,或者基类不应该有该方法。 也许你可以从该类中取出insert() ,将它移到子类中,并且需要insert(Object)扩展它的类,而那些需要insert(Object, Object)的类扩展基础对象的不同子类。

我不相信有一种干净的方法来完全隐藏Java中的inheritance方法。

在这种情况下,如果你绝对不能支持这种方法,我可能会在子类中将该方法标记为@Obsolete,并让它抛出一个NotImplementedException(或Java中的等效exception),以阻止人们使用它。

最后,如果你inheritance了一个对你的子类没有意义的方法,那么你真的不应该inheritance那个基类。 它也可能是基类设计不当或包含太多行为,但可能值得考虑您的类层次结构。 另一个要查看的路径可能是组合,其中您的类具有以前是基类的私有实例,您可以通过将它们包装在您自己的方法中来选择要公开的方法。 (编辑:如果基类是抽象的,组合可能不是一个选项……)

正如Cletus指出的那样,这实际上是一个设计问题,因为您正在尝试创建一个不遵守其父类合同的子类。

在极少数情况下,通过例如抛出exception来解决这个问题可能是可取的(或者至少是一个可接受的折衷方案 – 例如,Java Collections Framework),但总的来说它是设计不佳的标志。

您可能希望阅读Liskov替换原则 :(如维基百科所说)“如果S是T的子类型,则程序中类型T的对象可以替换为S类型的对象而不更改任何该计划的理想属性“。 通过重写抛出exception的方法,或以任何其他方式隐藏它,你违反了这个原则。

如果基类’方法的契约是“插入当前对象,或者抛出exception”(参见例如JavaDoc for Collection.add() )那么你可能会认为你没有违反LSP,但如果这是意外的大多数来电者您可能希望在这些理由上重新考虑您的设计。

这听起来像一个设计糟糕的层次结构 –

如果不存在默认值且用户根本不应该调用该方法,则可以将该方法标记为@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; } } 

但是,如果存在合理的默认值,则可以将inheritance的方法标记为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) { // ... } }