20

我需要两个可以访问彼此私人的实例。我自然想到了一个伴生对象,它允许访问它的伴生类的唯一实例。我将类本身设为私有,因此用户不能只使用new.

object A {
    def apply = dual
    lazy val dual = new A
}

private class A {
    //some irrelevant logic...
}

此代码无法编译。我明白了:A 类作为 A 类错误的一部分逃脱了它的定义范围,我不太明白。我目前的解决方法是在类应该具有的每个方法声明中定义一个特征,并class A扩展该特征,而dual是特征类型,而不是class A类型。

我在这里缺少什么理论问题?为什么这是禁止的?

4

2 回答 2

29

Paolo 的解决方案很好 (+1),但他没有解释错误信息,所以让我试试。问题源于每个方法都需要返回类型这一事实。您的原始定义applydual返回了一个对象class A,因此两者的隐式返回类型都是A。这意味着A必须对客户可见——他们还能如何调用函数或访问val? 此外,由于两者——以及它们的父对象——都是公共的,它们是全局可见的。但是,您声明A private这意味着它不能在其包外可见。因此存在编译器无法解决的冲突。

一般规则是函数/成员的所有参数和返回类型必须(至少)与引用成员本身具有相同的可见性范围*。因此,解决此问题的一种简单方法是降低 和 的可见apply性。这会满足编译器,但不是你:-)dualprivate

您的解决方案通过将静态返回类型更改为public特征来解决问题,因此与引用它的成员具有相同的可见性。返回对象的动态类型仍然是class A,但是,这不需要对客户端可见。这是“程序到接口,而不是实现”原则的经典示例。

请注意,要完全应用此原则,可以class A变成 的private内部类object A,从而使其即使对于同一包中的其他类也无法访问:

trait A {
    //...
}

object A {
    def apply: A = dual
    lazy val dual: A = new AImpl

    private class AImpl extends A {
        //some irrelevant logic...
    }

}

*为了迂腐,封闭的类/对象可能会降低其成员的可见性,如下所示:

private class Holder {
  def member = new Hidden
}

private class Hidden

where memberispublic但它的封闭类 is private,有效地将其成员隐藏在外部世界之外。所以编译器在这里不会发出任何抱怨。

于 2013-01-14T15:11:45.883 回答
25

我认为您不想要一个私有类,而是一个具有私有构造函数的类。

class A private() 
object A {
    def apply = dual
    lazy val dual = new A
}

现在你的类对外部代码是“可见的”,但只有你的伴生对象可以创建它的实例。

于 2013-01-14T14:51:30.330 回答