In Scala, in addition to the usual access modifier private, there is also the modifier private [this]. These two modifiers are quite similar to each other. In addition, in Java there is only a simple private. Therefore, they can easily cause confusion or the conviction that a simple private is not so private, and everywhere it is necessary to use private [this] for greater security. But let's see how things really are.
In the article, for brevity, I will mainly use the term
private variable . But in fact, it will mean a
private class property or a private class method.private
Let's start with the usual private. Let's see what level of security it provides us. The easiest thing to check is with examples. To run the code, I used the scala workseet in Intellij IDEA. My version of Scala is 2.11.7.
Suppose we have a class WithPrivate with a private field myPrivateVal and a method that returns the value of this field:
')
class WithPrivate { private val myPrivateVal = 5 def show = myPrivateVal }
Let's try calling the show method:
val a = new WithPrivate a.show
And we see the result:
res0: int = 5
All as expected -
you can use a private property inside the class .
Now let's try to access a private variable from outside the class:
val a = new WithPrivate a.myPrivateVal
The compiler gives an error:
Error: (9, 4) value myPrivateVal in class WithPrivate cannot be accessed in A $ A86.this.WithPrivate
That is,
we cannot access a private variable from outside the class .
Let's check how the heirs behave. Let's try to access the private variable inside the heir:
class B extends WithPrivate { def usePrivate = { myPrivateVal } }
We see the error:
Error: (8, 6) not found: value myPrivateVal
Again, the expected result -
we can not use private variables in the heirs .
Let's try to override the private variable in the heir:
class C extends WithPrivate { override val myPrivateVal = 10 }
And again the error:
Error: (22, 17) value myPrivateVal overrides nothing
It turns out that this can not be done. But what if you remove override:
class D extends WithPrivate { val myPrivateVal = 10 } val d = new D d.show
In the console we see:
res0: int = 5
That is, the redefinition failed. We simply created a variable with the same name in the heir. Thus,
we also cannot override the private variable in the heir.As you can see, the behavior of the private modifier corresponds to how it behaves in Java. That is the opinion that the usual private is not too private - is wrong. Regular private at least provides the same level of closeness as private in Java.
private [this]
Next in line is private [this]. But before reviewing it, I would like to remind you what this record generally means:
class A { private[XXX] val someXxxVal = 5 }
In place of XXX can be:
- Package name In this case, all classes in the XXX package have access to someXxxVal. This feature is often used inside libraries when they want a property or method to be visible only inside the library and not stick out. It can also be useful to get to the internal state of an object in unit tests.
- The name of the class / object . In this case, the XXX class / object will have access to someXxxVal.
- this . About him further.
Now let's see how private [this] behaves. The easiest way is to apply the same tests that we have already used for the ordinary private. I will not again paint each case separately, just give a general listing:
class WithPrivateThis { private[this] val myPrivateVal = 5 def show = myPrivateVal } val a = new WithPrivateThis
If you run this code, you can make sure that these tests show absolutely similar results. That is,
private [this] in these tests shows the same behavior as regular private . But if the results are the same, then why do you even need private [this]?
But we have Scala, not Java.
The fact is that the tests above only cover cases that come from private behavior in Java. But for Scala these are not all cases of using private
Let's look at the following example:
object WithPrivate { def foo = (new WithPrivate).myPrivateVal } class WithPrivate { private val myPrivateVal = 5 } WithPrivate.foo
Here we created a companion object for our class and use the private variable inside it. This example will compile normally and show the result:
res0: int = 5
Let's try the same with private [this]:
object WithPrivateThis { def foo = (new WithPrivateThis).myPrivateVal } class WithPrivateThis { private[this] val myPrivateVal = 5 } WithPrivateThis.foo
In this example, the compiler will curse:
Error: (13, 36) value $ A113.this.WithPrivateThis
This is the first difference -
in the case of private [this], we cannot use a variable in the companion object . The object cannot access until myPrivateVal.
Now look at this case. Suppose we have a method in our class that accepts an instance of the same class as input. What if we turn to a private variable in this object?
class WithPrivate { private val myPrivateVal = 5 def withThat(that: WithPrivate) = { that.myPrivateVal } } val a = new WithPrivate val b = new WithPrivate b.withThat(a)
In the withThat method we try to reach the private variable of another instance (that) of the same class (WithPrivate).
If you run this code, you will see the expected result:
res0: int = 5
Such a technique is often used, for example, in equals. If we omit that myPrivateVal is the same for all instances of the WithPrivate class, then for it equals could look like this:
override def equals(obj: Any) = obj match { case that: WithPrivate ⇒ this.myPrivateVal == that.myPrivateVal case _ ⇒ false }
Now let's try the same with private [this]:
class WithPrivateThis { private[this] val myPrivateVal = 5 def withThat(that: WithPrivateThis) = { that.myPrivateVal } } val a = new WithPrivateThis val b = new WithPrivateThis b.withThat(a)
And what do we see? Mistake!
Error: (21, 11) myPrivateVal is not a member of A $ A151.this.WithPrivateThis
That is, the
compiler does not allow us to use a private variable with private [this] from another object in the same class . And we will not be able to implement equals in the manner described above for the WithPrivate class.
Eventually
If we summarize the two previous examples, we can say that the ordinary private can be considered as private [ThisClass], where ThisClass is the class in which the private variable is declared. That is, the level of visibility is limited to the current class / object. All instances of the class ThisClass and companion object of this class will see this private variable.
private [this] should be considered as privacy at the level of a specific class instance. That is, we can access the private variable only within the current instance of the class. Neither other class nor companion object has access to such a private variable. We can say that private [this] is a stricter version of the usual private.
In which cases, which modifier to use? I try to follow the following rules:
- If you have a mutable private variable that stores a state, then it is better to make it private [this]. This will not allow other instances of the class or companion to change the state of this variable, which can sometimes be fatal for the general state of the object.
- If you have a method whose call changes the state of an object, it is also best to make it private [this].
- In all other cases, use the usual private.
In conclusion, I want to add that in a normal project almost always there is a simple private one. If you write the code carefully, then private [this] can not be used at all. Not many classes have methods that accept instances of the same class as input. And who in such methods will call destructive private methods or change the private state in other objects? That is, if you try to protect yourself everywhere by laying around private [this], then this may simply not be useful with the proper level of discipline in the code. At the same time, the active use of private [this] can be very useful inside libraries.