Surprising behavior with protected value

Imagine the following scenario:

class Parent(x: Int) {
  protected final val y: Int = x + 1

  def combine(that: Parent): Parent =
    new Parent(this.x + that.y) // This compiles
}

class Sub(x: Int) extends Parent(x) {      
  override def combine(that: Parent): Parent =
    new Sub(this.y + that.y) // This does not compile, due that.y
}

It won’t compile due to this error:

value y in class Parent cannot be accessed in Parent
Access to protected value y not permitted because
prefix type Parent does not conform to
class Sub where the access takes place

IMHO, this is not what one would expect and I even thought it was a bug, but @SethTisue and @som-snytt confirmed that this is the correct behavior according to the specs.

Also, I would like to point out that I am not the only - one surprised by this.

1 Like

Overriding vals should be made illegal. Most of the time the result is not what was intended.

2 Likes

That is not the point, also the code example is not intended to show any real use case, but a simplification to show the problem.
(BTW, I edited the code example to remove the override of the val)

The point is that I can not access the y of another instance of Parent inside Sub, but I can access my own y. Which, IMHO, doesn’t make sense, since protected should be more open than private. Also, as you can see, I can access the protected y of another instance of Parent inside Parent itself, the restriction only applies when on a subclass.

2 Likes

I find it confusing as well. While I’m not an expert, I would expect the semantics of protected to mean “any subclass can see this freely”. Otherwise I would naively assume I would need to use protected[this]

2 Likes

If you find protected useful, then one way to think about the information hiding is:

A protected member is visible in a subclass; it is part of the contract with the subclass.

Obviously, protected is more open than private, because the member is also visible in a subclass.

It is not visible between instances of disparate subclasses. The two class hierarchies could be very different in behavior.

However, if coordination between subclasses is deemed useful, then it’s easy to widen access to an enclosing template or package.

scala> trait Top { protected type A ; protected def f: A }
defined trait Top

scala> class C extends Top { protected type A = Int ; protected def f = 42 ; def g[B <: C](b: B): A = b.f }
defined class C

scala> class C extends Top { protected type A = Int ; protected def f = 42 ; def g[B <: Top](b: B): A = b.f }

// error, the result of b.f is b.A not C#A.

scala> class D extends Top { protected type A = String ; protected def f = "forty-two" }
defined class D

Martin Odersky used contracts to argue against private[this] as a mere optimization. Simple private is already a contract with consumers of the class, that they can’t access this member. A class doesn’t need a contract with itself. People don’t write contracts with themselves except in the form of New Year’s Resolutions, which are inevitably broken.

1 Like