2009-12-14 78 views
11

我有一个类结构,我希望基类中的某些方法可以从直接从基类派生的类访问,但不能派生自派生类。根据Java语言规范,可以覆盖继承方法的访问规范,使它们更公开,但不是更私密。例如,这是我需要做的要点,但是非法:是否可以在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 { 

} 

有什么办法可以做到这一点吗?

编辑解释为什么我想这样做:

在这种情况下,类结构是数据处理和进口结构。它读入并分析充满表格数据的文本文件,然后将它们存储在数据库中。

基类是管理数据库处理部分的基表类。其中包含相当数量的功能,这对所有表类型都很常见 - 因为一旦它们在数据库中,它们就会变得统一。

中间类是特定于正在解析的文件中的表的类型,并具有表解析和导入逻辑。它需要访问一些基类的数据库访问函数。

顶级类是特定于表的,只不过是以父类可以理解的方式初始化表的布局。此外,基类的用户不需要查看或访问中间类所具有的数据库特定功能。实质上,我想把这些函数只展现在基类之上的一层,而不是其他人。

我问,因为虽然我作为例子发布的代码是非法的,但可能还有其他一些手段来完成相同的目的。我问是否有。

也许隐藏是一种错误的方式来描述这个 - 我真正需要做的是将一些应该是基类私有的功能公开到层次结构中上一级的类中。隐藏会做到这一点 - 但我可以看到隐藏是一个问题。有没有另一种方法来做到这一点?

+2

由于DerivedOne是一个基地,它必须容纳基地的合同。顺便说一下,这就是为什么委托(复合模式)通常比继承更受欢迎的原因。 – 2009-12-14 18:03:31

+0

@Steve我不相信复合模式可以在这里工作。从本质上讲,我只需要将一些对基类应该是私有的功能展示给一个级别的类。 – 2009-12-14 18:33:39

+0

更新了我的答案。 – rsp 2009-12-14 19:56:27

回答

15

我认为这个问题的本质为你带来它暴露了你的对象模型的概念问题。你试图将各种单独的责任描述为“是”的关系,而实际上你应该做的是描述“拥有”或“使用”关系。事实上,你想从一个子类隐藏基类功能告诉我这个问题实际上并没有映射到一个三层继承树上。

这听起来像你正在描述一个经典的ORM问题。让我们再看看这个,看看我们是否可以把它重新映射到比严格其他概念“是一个”继承,因为我真的认为你的问题不是技术问题,它的概念:

你说:

的基类是基表类 管理数据库处理的 部分它。有包含在它的 功能有相当数量是共同 所有表类型 - 因为一旦 他们在数据库中,他们成为 均匀。

这可能更清晰,但它听起来像我们有一个类需要管理数据库连接和常见数据库操作。在Single Responsibility之后,我想我们已经完成了。你并不需要扩展这个类,你需要它需要使用它的功能的类。

中产阶级是特定的文件是 解析在 样的表,并具有表解析和 进口逻辑。它需要访问基类的数据库访问 函数的一些 。

这里的“中产阶级”听起来有点像Data Mapper。这个类不需要扩展上一个类,它需要拥有对它的引用,或许注入构造函数或setter作为接口。

顶层类是特定于 表,并确实没有什么比 更在某种程度上 父类可以理解初始化表的布局。 此外,用户还基类的不 需要看或访问哪些中间 类做数据库 特定功能。实质上,我想揭示 这些函数只能在基类之上的一个级别 而没有其他人。

我不清楚为什么一个高层次的类似乎有关于db模式的知识(至少这就是“初始化表格布局”这个词给我的建议),但是,如果前两节课是encapsulation ("has a"/"uses a") instead of inheritance ("is a"),我不认为这会是一个问题。

+0

你提出了一些好点,它肯定给了我一些想法。我遇到的问题是,我处理的情况并不适用于封装。我担心我无法提供更多信息 - 这是工作和专有的,我实际上对我给出的数额有些担忧。不过,我感谢你的见解,并且我会仔细观察我的结构。 – 2009-12-15 17:22:42

+1

其实现在我已经回顾了自己的结构,看起来我的问题实际上是由星期一 - 下午睡眠剥夺脑失败和咖啡因不足造成的。事实证明,我想隐藏上层课程的方法是为了让他们看到,以防他们需要覆盖它。我不知道为什么我在想别的。 无论如何,要记住未来设计的经验法则。如果有人认为需要隐藏 - 一个人的设计是错误的。看封装。感谢您的周到和详细的答案。 – 2009-12-15 18:05:19

10

号我不知道为什么你会引用规范,然后问是否有没有办法做的规范说的正好相反......

也许如果你解释为什么希望要做到这一点,你可以得到如何一些建议。

+0

好的,隐藏是解释这个错误的方法。虽然这将完成我需要的结果。一个更好的方式来解释这是从另一个方向:我需要公开一些私有基类的功能,直接从它派生的类 - 但不是上面的类。 – 2009-12-14 18:39:58

6

当覆盖一个方法时,你只能使它更多public,而不是更私密。我不知道为什么你用的是“一般”

记住,订购从最低到最高限制:

public<protected<default<private 

是,“protected”比default较少限制的访问修饰符(无时使用修饰符),因此您可以覆盖标记为覆盖方法的默认方法为protected,但不能做相反的操作。

能: 您可以覆盖一个protected方法与public之一。

不能: 不能覆盖一个public方法与protected之一。

+0

你可以用'protected'在另一个包中覆盖'protected',这很奇怪。啊,'protected'是“怪异的”。 – 2009-12-14 17:23:46

+0

当然,您始终可以使用相同的访问修饰符来覆盖。爱尔康询问如何改变访问级别。 – andandandand 2009-12-14 17:25:34

+0

固定词“general”;)当我的头在编码时,不是我的力量。我知道这个规则 - 我问是否有办法解决这个问题。 – 2009-12-14 18:36:23

6

如果你这样做,然后DerivedOne不会是一个基地,从DerivedTwo的观点。相反,你想要的是一个包装类

//Uses myMethod but keeps it hidden 
public class HiddenBase { 
    private final Base base = new Base(); 
    private void myMethod(); 
    public void otherMethod() {base.otherMethod();} 
} 

不能访问基虽然这样的保护方法......

+0

只有私人最终基地=新基地();'。 – 2009-12-14 17:24:29

+0

啊,谢谢,我会解决这个问题。 – KernelJ 2009-12-14 17:25:41

2

你描述的靠近该protected接入类是什么,派生班级可以访问,其他人则无法访问。

如果从基类继承你有过这种没有控制可能会带来一个问题,你可以同时调用超直接,类似使提供给你的类继承的代码抛出一个异常使该方法inaccesible给他人:

// 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() 
} 
+0

嗯......这是丑陋的。是的,保护接近,但不是那里。好主意,但。去只是不可见。如果我必须解决受保护的指导人们忽略它们,那也没关系。有点希望有一种方法可以有限度地揭示功能,只是为了清洁的缘故。 – 2009-12-14 18:44:27

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() 
    } 
} 
+0

接口不会执行此操作,因为存在所有写入基类的功能,而不仅仅是一个接口。请参阅编辑以更好地解释我实际尝试做的事情。 – 2009-12-14 18:42:33

2

这是可能的,但需要一些包装操作的,并可能导致是更复杂一点比你想的结构与长期合作。

考虑以下几点:


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... 
    } 
} 

我建议是对自己好一点,你的同事和其他任何人说结束不得不维护你的代码;重新考虑你的类和接口以避免这种情况。

1

你必须做出最后的方法时,将其覆盖

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. 
} 
相关问题