我碰巧发现它不允许有抽象的私人字段 特征,即
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已经把它变成了另一个有趣的东西 问题并提供了解决问题的模式。
答案 0 :(得分:8)
在允许子特征初始化的同时保持val私有的一种简单方法是将其定义为私有,但使用另一个受保护方法返回的值对其进行初始化。 然后子特征可以定义这个受保护的方法,以便更改初始值,但不能访问该值本身。 所以你会改变这个:
trait A {
protected val foo: Bar
}
进入:
trait A {
private val foo: Bar = initFoo
protected def initFoo: Bar
}
现在,只有特质A
可以访问val foo
。子特征可以通过definint foo
设置initFoo
的初始值,但无法访问foo
本身:
trait B extends A {
protected def initFoo: Bar = ???
}
显然,initFoo
本身仍然可以通过子特征访问。
如果initFoo
每次都创建一个新实例(换句话说,它是一个工厂),这通常不是问题,
因为我们可能只对将实例设为私有A
感兴趣,而不关心子特征是否能够创建Bar
的新实例
(不论新实例是否等于foo
方法的equals
。
但是如果它是一个问题(在你的情况下肯定是因为seed
是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提供的包私有解决方案当然是一个更容易的替代方案(尽管它不会阻止任何人在同一个包中定义他们的子特征,因此会破坏访问权限)限制)。
答案 1 :(得分:6)
您可以做的最好的事情是声明这些包私有 - private[package_name]
。
这将允许您在执行实现的同一个包中扩展和定义特征,但不允许客户端从其他包中使用它。