0
votes
object P{
  object P1{
    class A{
      //I want only classes/objects that extends A AND in the package P1 can access x
      //It means, the modifier `protected` and the qualifier [P1] is combined with operator `AND`:
      //protected AND [P1] means, x is:
      //    protected: not in subclass then not accessible
      //    AND [P1]: not in [P1] then not accessible
      //protected OR [P1] means, x is:
      //    protected: not in subclass then not accessible
      //    OR [P1]: not in [P1] then not accessible
      protected[P1] val x = 1

      //Like `protected[this]`: y is protected AND [this]
      //because y is accessible only in subclass AND in the same object
      //(access to y in B2.f2 is permit but in B2.f3 is deny)
      //(if protected[this] == protected OR [this] then protected[this] == protected :D)
      protected[this] val y = 2

      //Also, I don't know why the following code is valid
      //(scalac 2.10.0 compile it!). Is this an error in scala compiler?
      //But this strange modifiers combination is also not what I want!
      private[P1] protected val z = 1
    }
    class B{
      def f(a: A) = a.x + a.z //permit!
    }
  }
  object P2{
    class B2 extends P1.A{
      def f = x + z //also permit!
      def f2 = y //permit. OK
      def f3(b: B2) = b.y //deny. OK
    }
  }
}

I know that protected[P1] modifier on x is same as java's protected. But, how to allow access to A.x only from classes/objects that extends A AND in the package P1?

EDIT: @Randal ask: "Why do you care about the package constraint? What does that get you?"

I have a large project with a complex class. I split the class to several trait. But some members in some traits is intend to be used only in some (but not all) other sub-traits. So, I organize all traits that need the accessibility in one package. And the instantable class that need logic of those traits is put in another package. But the class need access to only some traits' members. Then I want only the needed members is visible to the class:

package p.base
private[base] trait A{
  //x is intent to be used only in trait B and C
  protected[base] val x = 1
}
private[base] trait B{this: A =>
  //f is intent to be used only in trait C
  protected[base] def f = x
  //f2 will be used in global.D
  def f2 = f
}
private[p] trait C extends B with A{...}

package p.global
class D extends p.base.C{
  def g = f2
}
2
Why do you care about the package constraint? What does that get you?Randall Schulz
Traits define types. Concrete classes that implement / extend that trait cannot revoke access to members defined in that trait. That is a fundamental property of what we call "object-oriented" software.Randall Schulz
I do NOT want / try to revoke accessibility. I want a way to define accessibility right in the traitBùi Việt Thành

2 Answers

0
votes

It's not possible using specialized access modifiers only. To get the compile time restrictions you could use a technique like this

object P {
  object P1 {

    class A {
      @implicitNotFound(msg = "Only accessible when extending from A")
      sealed trait OnlyA
      protected[this] implicit object OnlyA extends OnlyA

      private[P1] def x(implicit ev: A#OnlyA) = 1

      private[P1] val z = 1
    }

    class B {
      def f(a: A) = a.z + a.x         // Will not work
    }
    class C extends A {
      def f(a: A) = a.z + a.x         // Works
    }
  }
  object P2 {

    class B2 extends P1.A {
      def f = x                       // Will not work
      def f(a: P1.A) = a.x + a.z      // Will not work
    }
  }

}

0
votes

You can protect the constructor with private[PkgName] and supply a factory to provide public creation so subclassing is confined to that package:

package pkg
...
  class A private[pkg] { ...
  }

  object A {
    def apply(): A = new A
  }
  ...