私有类的伴侣对象:为什么它不是有效的?

gil*_*och 18 types scala companion-object

我需要两个可以访问彼此私有的实例.我天生就想到了一个伴侣对象,它允许访问它的同伴类的唯一实例.这个类本身是私有的,因此用户不能只使用创建实例new.

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

private class A {
    //some irrelevant logic...
}
Run Code Online (Sandbox Code Playgroud)

此代码无法编译.我得到:类A逃避其定义范围作为类型A错误的一部分,我真的不明白.我当前的解决方法是使用类应该具有的每个方法声明来定义特征并使class A该特性扩展,而双重属于特征类型,而不是class A类型.

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

Pét*_*rök 29

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

一般规则是函数/成员的所有参数和返回类型必须具有(至少)与引用成员本身*相同的可见范围.因此,一个解决这个问题的琐碎办法是减少的知名度applydualprivate.这会满足编译器,但不是你:-)

您的解决方案通过将静态返回类型更改为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...
    }

}
Run Code Online (Sandbox Code Playgroud)

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

private class Holder {
  def member = new Hidden
}

private class Hidden
Run Code Online (Sandbox Code Playgroud)

这里memberpublic但它的外围类private,从而有效地隐藏其成员从外部世界.所以编译器不会在这里发出任何抱怨.


Pao*_*lla 24

我认为你不想要一个私有类,而是一个带私有构造函数的类.

class A private() 
object A {
    def apply = dual
    lazy val dual = new A
}
Run Code Online (Sandbox Code Playgroud)

现在,您的类对外部代码"可见",但只有您的伴随对象可以创建它的实例.