我碰巧发现不允许在特征中使用抽象的私有字段,也就是说,
trait A1 {
//private val a: Int // Not allowed
protected val b: Int // OK
}
Run Code Online (Sandbox Code Playgroud)
如果私有字段是构造函数参数,那么对抽象类做这样的事似乎是可以的,也就是说,
abstract class A2 (private val i: Int) // OK
Run Code Online (Sandbox Code Playgroud)
所以我猜一个特征没有构造函数参数,因此没有办法初始化它们,因此不允许使用抽象的私有字段.
如果它们是"受保护的",则子类可以使用预先初始化的字段初始化它们.此方法允许子类查看这些字段.
如果我只想初始化它们并在之后隐藏它们,如下例所示,该怎么办?
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]
}
Run Code Online (Sandbox Code Playgroud)
我不希望任何人能够看到种子,也就是说,[2](等等[1])不应该被允许.是否有一种方法可以做到这一点?
正如@Randall和@ pagoda_5b指出的那样,我的问题没有多大意义.但幸运的是,@Régis和@ axel22已经把它变成另一个有趣的问题并提供了一个解决它的模式.
在允许子特征初始化的同时保持val私有的一种简单方法是将其定义为私有,但使用另一个受保护方法返回的值对其进行初始化.然后子特征可以定义这个受保护的方法,以便更改初始值,但不能访问该值本身.所以你会改变这个:
trait A {
protected val foo: Bar
}
Run Code Online (Sandbox Code Playgroud)
成:
trait A {
private val foo: Bar = initFoo
protected def initFoo: Bar
}
Run Code Online (Sandbox Code Playgroud)
现在,只有trait A
可以访问val foo
.子特征可以foo
通过definint 设置初始值initFoo
,但不能访问foo
自身:
trait B extends A {
protected def initFoo: Bar = ???
}
Run Code Online (Sandbox Code Playgroud)
显然,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
}
Run Code Online (Sandbox Code Playgroud)
现在,如果B
尝试调用initFoo
,编译器将抱怨它找不到隐式类型InitA
(唯一的这样的实例是A.initA
并且只能访问A
).
正如我所说,它有点糟糕,axel22给出的包私有解决方案当然是一个更容易的替代方案(虽然它不会阻止任何人在同一个包中定义他们的子特征,因此会破坏访问限制).