0
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 Answers2

0

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
    }
  }

}

EECOLOR
  • 11,184
  • 3
  • 41
  • 75
  • Oh. Your solution is very good, but not perfect :D. Do you think that scala should provide a less verbose solution for this use case? – Bùi Việt Thành Feb 26 '13 at 04:24
  • access qualifiers is to allow access ONLY in the right places. It has 2 directions: horizontal (package [p] (and sub-packages)) and vertical (sub classes (or sub-traits)). Sometimes we want to combine the qualifiers with "OR" (like protected[p] = java's protected). But sometimes we need "AND" (my use case). – Bùi Việt Thành Feb 26 '13 at 04:35
  • Your solution is not practical: 1. We must add `(implicit ev: A#OnlyA)` to many methods. 2. To apply this technique to fields, we need an even more verbose stupid code. – Bùi Việt Thành Feb 26 '13 at 04:44
  • I agree it's not practical. As Randall Schulz mentioned, what you want is not very conventional. I showed you how you could get the compiler warnings you wanted. I however recommend you organize your code differently. – EECOLOR Feb 26 '13 at 18:04
  • Hmm. Do you have any suggestion about how to organize my code? I still think this is a limit of scala. – Bùi Việt Thành Feb 27 '13 at 04:48
  • You can make public interfaces that contain less methods. You can also create a proxy object that people can extend. – EECOLOR Feb 27 '13 at 17:25
0

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
  }
  ...
Randall Schulz
  • 26,420
  • 4
  • 61
  • 81
  • @BùiViệtThành: Please amend your question. Code is unreadable in comments. (And please, put a space before you open braces.) – Randall Schulz Feb 26 '13 at 02:45
  • package a: class A private[a]{ val x = 1}. class B extends A. package b: class C extends B{ def f = x}. I want deny access to x in B. How? – Bùi Việt Thành Feb 26 '13 at 02:59
  • I'm sorry... Please put the amendment to the question _in the original question_. I'm not going to re-format code in a comment to make it readable. However, you simply cannot extend a class (or trait) and reduce access—It violates the Liskov Substitution Principle, for one thing. – Randall Schulz Feb 26 '13 at 03:12
  • my comment above is only for replying your answer. The idea in the comment is in the original question already. – Bùi Việt Thành Feb 26 '13 at 03:52