【问题标题】:Abstract private fields in Scala traitScala trait 中的抽象私有字段
【发布时间】:2013-04-05 14:19:12
【问题描述】:

我碰巧发现里面不允许有抽象的私有字段 一个特质,也就是,

trait A1 {
    //private val a: Int         // Not allowed
    protected val b: Int         // OK
}

如果是私有字段,对抽象类做这样的事情似乎没问题 是构造函数参数,即

abstract class A2 (private val i: Int) // OK

所以我猜一个特征没有构造函数参数,所以没有 初始化它们的方式,因此不允许抽象私有字段。

如果它们是“受保护的”,那么子类可以使用预初始化来初始化它们 字段。这种方法允许子类看到这些字段。

如果我只想初始化它们然后隐藏它们怎么办? 如下例所示?

object holding {
    trait trick {
        protected val seed: Int                     // Can't be private
        final def magic: Int = seed + 123
    }

    trait new_trick extends trick {
        def new_magic: Int = magic + 456
        def the_seed: Int = seed                    // [1]
    }

    def play: new_trick = new { val seed = 1 } with new_trick 

    def show_seed(t: new_trick): Int = t.the_seed   // [2]
}

希望任何人都能看到种子,也就是说,不应允许 [2](因此 [1])。 有没有办法让特质做到这一点?


正如@Randall 和@pagoda_5b 所指出的,我的问题没有多大意义 感觉。但幸运的是 @Régis 和 @axel22 把它变成了另一个有趣的 问题并提供了解决问题的模式。

【问题讨论】:

  • 拥有私有且未实现的东西怎么可能有意义?这是一个矛盾,因为它不是继承的,也永远无法实现,因此会阻止实例化带有此类成员的特征的任何子类型。
  • 我不确定我是否关注你。如果您需要子类来定义种子(覆盖其定义),将其设为私有是没有意义的。也就是说,定义种子成员实现的子类将始终能够使用公共访问器显示它。我无法理解这种设计选择背后的需求。
  • 我认为归结为能够让直接子特征定义 seed 的值,同时使进一步的子特征(和外部代码)无法访问该值。有点像受保护的值(像对待子特征的受保护值一样对待,对于所有的事情都被视为私有的)。至于意义,我认为这个想法只是为了完全模拟可以通过参数(在类中)完成的操作,如他的示例所示。
  • 抱歉,我的意思是“有点像 one-level 保护值”

标签: scala traits


【解决方案1】:

在允许子特征初始化它的同时保持 val 私有的一种简单方法是将其定义为私有,但使用另一个受保护方法返回的值对其进行初始化。 然后子特征可以定义这个受保护的方法来改变初始值,但不能访问该值本身。 所以你会改变这个:

trait A {
  protected val foo: Bar
}

进入:

trait A {
  private val foo: Bar = initFoo 
  protected def initFoo: Bar
}

现在,只有 trait A 可以访问 val foo。子traits可以通过definint initFoo来设置foo的初始值,但不能访问foo本身:

trait B extends A {
  protected def initFoo: Bar = ???
}

显然,initFoo 本身仍然可以通过子特征访问。 如果initFoo 每次都创建一个新实例(换句话说,它是一个工厂),这通常不是问题, 因为我们可能只对将实例设为A 私有感兴趣,而不关心让子特征能够创建Bar 的新实例 (不管新实例是否等于foo,根据它们的equals 方法)。

但是,如果这是一个问题(在您的情况下肯定是 seed 是 fo 类型 Int,因此您要隐藏的是一个值而不仅仅是一个参考), 我们可以使用一个额外的技巧来允许子特征定义initFoo,但阻止它们(及其子特征)能够调用它。 让我们面对现实吧,这个技巧对于这种简单的需求来说非常糟糕,但它说明了高级访问控制的一个很好的模式。 感谢标准库作者的想法(请参阅http://www.scala-lang.org/api/current/index.html#scala.concurrent.CanAwait)。

trait A {
  // A "permit" to call fooInit. Only this instance can instantiate InitA
  abstract class InitA private[this]()
  // Unique "permit"
  private implicit def initA: InitA = null

  private def foo: Int = fooInit
  protected def fooInit( implicit init: InitA ): Int
}

trait B extends A {
  protected def fooInit( implicit init: InitA ): Int = 123
}

现在,如果B 尝试调用initFoo,编译器将抱怨它找不到InitA 类型的隐式(唯一的此类实例是A.initA,并且只能在A 中访问) .

正如我所说,这有点糟糕,并且 axel22 提供的包私有解决方案肯定是一个更容易的选择(尽管它不会阻止任何人在与您相同的包中定义他们的子特征,从而击败访问限制)。

【讨论】:

  • “现在,如果 B 尝试调用 foo,编译器将...”,您的意思是“如果 B 尝试调用 fooInit”吗?
【解决方案2】:

你能做的最好的就是将这些包声明为私有 - private[package_name]。 这将允许您在执行实现的同一包中扩展和定义特征,但不允许客户端从其他包中使用它。

【讨论】:

    猜你喜欢
    • 2014-06-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多