我有一个类结构,我希望基类中的某些方法可以从直接从基类派生的类中访问,但不能从派生类派生的类中访问。根据Java语言规范,可以覆盖继承方法的访问规范,使其更公开,但不是更私密。例如,这是我需要做的事情的要点,但这是非法的:
// Defines myMethod
public class Base {
protected void myMethod() {}
}
// Uses myMethod and then hides it.
public class DerivedOne extends Base {
@Override
private void myMethod();
}
// can't access myMethod.
public class DerivedTwo extends DerivedOne {
}
有没有办法实现这个目标?
编辑解释我为什么要这样做:
在这种情况下,类结构是数据处理和导入结构。它读入并解析充满表格数据的文本文件,然后将它们存储在数据库中。
基类是管理数据库处理部分的基表类。其中包含了相当多的功能,这些功能对于所有表类型都是通用的 - 因为一旦它们在数据库中,它们就变得统一。
中间类特定于要解析的文件中的表类型,并具有表解析和导入逻辑。它需要访问一些基类的数据库访问函数。
顶级类特定于表,并且只是以父类可以理解的方式初始化表的布局。此外,基类的用户不需要查看或访问中产阶级所执行的数据库特定功能。实质上,我想将这些函数仅显示在基类之上的一个级别而不是其他人。
我问,因为虽然我发布的代码是非法的,但可能还有其他方法可以实现同样的目的。我问是否有。
也许隐藏是错误的方式来表达这一点 - 我真正需要做的是将一些应该是基类专用的功能暴露给层次结构中的一级别的类。隐藏会实现这一点 - 但我可以看到隐藏是一个问题。还有另一种方法吗?
答案 0 :(得分:17)
我认为问题的本质就像你提出的那样暴露了对象模型的概念问题。您试图将各种不同的职责描述为“是一种”关系,而实际上您应该做的是描述“有一个”或“使用”关系。您希望从子类隐藏基类功能这一事实告诉我,这个问题实际上并没有映射到三层继承树。
听起来你正在描述一个典型的ORM问题。让我们再看一遍,看看我们是否可以将它重新映射到其他概念而不是严格的“是”继承,因为我认为你的问题不是技术问题,而是概念性的:
你说:
基类是基表类 管理数据库处理的一部分 它。有相当数量的 包含在其中的功能 所有表类型共有 - 一次 他们在他们成为的数据库中 均匀的。
这可能更清楚,但听起来我们有一个类需要管理数据库连接和常见的数据库操作。在Single Responsibility之后,我想我们已经在这里完成了。您不需要扩展此类,您需要将移动到需要使用其功能的类。
中产阶级是特定的 文件中的那种表 解析,并具有表解析和 导入逻辑。它需要访问一些 基类的数据库访问 功能
这里的“中产阶级”听起来有点像Data Mapper。这个类不需要扩展前一个类,它需要拥有对它的引用,可能在构造函数或setter上作为接口注入。
顶级课程特定于 表并没有什么比 以某种方式初始化表的布局 父类可以理解。 基类的用户也没有 需要查看或访问数据库 中间的具体功能 上课。从本质上讲,我想揭示 这些功能仅限于一个级别 在基类之上,没有其他人。
我不清楚为什么高级类似乎对db模式有所了解(至少那是“初始化表格的布局”这句话对我的意思),但是再次,如果前两个之间的关系课程是encapsulation ("has a"/"uses a") instead of inheritance ("is a"),我不认为这会是一个问题。
答案 1 :(得分:10)
没有。我不确定你为什么引用这个规范,然后问是否有任何方法可以做出与规范相反的方式......
也许如果你解释为什么你想要这样做,你可以得到一些关于如何的建议。
答案 2 :(得分:6)
覆盖方法时,您只能将其设为公开,而不是更私密。我不知道你为什么使用“一般”这个词
请记住,从最少限制到最严格的排序:
public<protected<default<private
是的,“protected
”是一个限制性较小的访问修饰符而不是default
(当没有使用修饰符时),因此您可以覆盖将覆盖方法标记为protected
的默认方法,但不要反其道而行之。
<强>可以:强>
您可以使用protected
覆盖public
方法。
<强>不能:强>
您无法使用public
覆盖protected
方法。
答案 3 :(得分:6)
如果你这样做,那么从DerivedTwo的角度来看,DerivedOne不会是Base。相反,你想要的是一个包装类
//Uses myMethod but keeps it hidden
public class HiddenBase {
private final Base base = new Base();
private void myMethod();
public void otherMethod() {base.otherMethod();}
}
你不能通过这种方式访问基地的受保护方法......
答案 4 :(得分:3)
继承有效,因为在任何地方都可以使用基类,也可以使用其中一个子类。行为可能不同,但API不是。这个概念称为the Liskov substitution principle。
如果您能够限制对方法的访问,则生成的类将不具有相同的API,并且您将无法使用替换其中一个派生类的基类实例,从而否定继承的优势。
您真正想要实现的目标可以通过界面完成:
interface IBase1 {
}
class Derived1 implements IBase1 {
public void myMethod() {
}
}
class Derived2 implements IBase1 {
}
class UseMe {
public void foo(IBase1 something) {
// Can take both Derived1 and Derived2
// Can not call something.myMethod()
}
public void foo(Derived1 something) {
something.myMethod();
}
public void foo(Derived2 something) {
// not something.myMethod()
}
}
答案 5 :(得分:3)
这是可能的,但是需要一些包操作,并且可能导致结构比你想要长期使用的结构复杂一点。
考虑以下事项:
package a;
public class Base {
void myMethod() {
System.out.println("a");
}
}
package a;
public class DerivedOne extends Base {
@Override
void myMethod() {
System.out.println("b");
}
}
package b;
public class DerivedTwo extends a.DerivedOne {
public static void main(String... args) {
myMethod(); // this does not compile...
}
}
我建议对自己,你的同事以及最终必须维护你的代码的任何其他人感到高兴;重新考虑您的类和接口以避免这种情况。
答案 6 :(得分:2)
您所描述的内容接近于protected
访问类的内容,派生类可以访问,其他所有内容都不能访问。
如果从基类继承,你无法控制它可能会造成问题,你可以通过抛出异常使其他人无法访问,同时通过直接调用super来使类继承的代码可用,例如: / p>
// Uses myMethod and then hides it.
public class DerivedOne extends Base {
@Override
public void myMethod() {
throw new IllegalStateException("Illegal access to myMethod");
}
private void myPrivateMethod() {
super.myMethod();
}
}
编辑:回答您的详细说明,如果我理解正确,您需要在中产阶级中定义的基类的上下文中指定行为。抽象受保护的方法对于从中产阶级派生的类是不可见的。
一种可能的方法是使用在基类中需要抽象的方法定义接口,在基类中保留私有最终引用,并在构造中间类对象时提供对实现的引用。
接口将在(静态?)嵌套在中间类中实现。我的意思是:
public interface Specific {
public void doSomething();
}
public class Base {
private final Specific specificImpl;
protected Base(Specific s) {
specificImpl = s;
}
public void doAlot() {
// ...
specificImpl.doSomething();
// ...
}
}
public class Middle extends Base {
public Middle() {
super(new Impl());
}
private Impl implements Specific {
public void doSomething() {
System.out.println("something done");
}
}
}
public class Derived extends Middle {
// Access to doAlot()
// No access to doSomething()
}
答案 7 :(得分:2)
你必须在覆盖它时使方法最终
public class Base {
protected void myMethod() {}
}
// Uses myMethod and then hides it.
public class DerivedOne extends Base {
@Override
final protected void myMethod(); //make the method final
}
public class DerivedTwo extends DerivedOne {
// can't access myMethod here.
}