I happened to find that it's not allowed to have abstract private fields in a trait, that is,
trait A1 {
//private val a: Int // Not allowed
protected val b: Int // OK
}
And it seems all right to do such thing to an abstract class, if private fields are constructor parameters, that is,
abstract class A2 (private val i: Int) // OK
So I guess that a trait doesn't have constructor parameters, so there's no way to initialize them, therefore no abstract private fields are allowed.
If they are "protected", then a subclass can initialize them using pre-initialized fields. This approach allows subclass to see these fields.
What if I just want to initialize them and hide them afterwards, as in the following example?
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]
}
I don't want anyone to be able to see seed, that is, [2] (and so [1]) shouldn't be allowed. Is there a way for a trait to do that?
As @Randall and @pagoda_5b have pointed out, my question doesn't make much sense. But luckily @Régis and @axel22 have turned it into another interesting question and provided a pattern for solving it.
Traits can have methods(both abstract and non-abstract), and fields as its members.
The first difference was already mentioned: classes are limited to inherit from a single abstract class but can inherit from multiple traits. Another important difference is that abstract classes allow specifying constructor parameters. Traits do not allow us to do the same.
Scala traits don't allow constructor parameters However, be aware that a class can extend only one abstract class.
Abstraction is the process to hide the internal details and showing only the functionality. In Scala, abstraction is achieved by using an abstract class. The working of the Scala abstract class is similar to Java abstract class. In Scala, an abstract class is constructed using the abstract keyword.
A simple way to keep a val private while allowing the sub-traits to initalize it would be to define it as private but initialize it with the value returned by another protected method. Then the sub-traits can define this protected method so as to change the initial value, but cannot access the value itself. So you would change this:
trait A {
protected val foo: Bar
}
into:
trait A {
private val foo: Bar = initFoo
protected def initFoo: Bar
}
Now, only trait A
can access the val foo
. Sub-traits can set the initial value of foo
by definint initFoo
, but cannot access foo
itself:
trait B extends A {
protected def initFoo: Bar = ???
}
Obviously, initFoo
itself is still accessible by sub-traits.
This is often not a problem if initFoo
creates a new instance everytime (in other words, it is a factory),
as we might only be interested in making the instance private to A
without being concerned with having sub-traits being able to create new instances of Bar
(irrespective of whether the new instances are equals to foo
as per their equals
method).
But if it is a concern (and it certainly is in your case as seed
is fo type Int
and thus what you want to hide is a value and not just a reference),
we can use an additional trick to allow sub-traits to define initFoo
but prevent them (and their sub-traits) from being able to call it.
This trick is, let's face it, pretty awful for such a simple need, but it illustrates a nice pattern for advanced access control.
Credits goes to the standard library authors for the idea (see 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
}
Now, if B
tries to call initFoo
, the compiler will complain that it could not find an implicit of type InitA
(the unique such instance is A.initA
and is only accesible to in A
) .
As I said, it's a bit awful and the package private solution given by axel22 is certainly a much easier alternative (although it won't prevent anyone from defining their sub-traits inside the same package as yours, thus defeating the access restriction).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With