Scala アクセス修飾子

Scala では、アクセス修飾子はクラス、オブジェクト、トレイト(traits)、およびそのメンバー(フィールドやメソッドなど)の可視性とアクセス権限を制御するために使用されます。

Scala のアクセス修飾子は基本的に Java と同じで、private、protected、public があります。

アクセス修飾子を指定しない場合、デフォルトでは Scala オブジェクトのアクセスレベルはすべて public です。

Scala の private 修飾子は Java よりも厳格で、ネストしたクラスの場合、外側のクラスはネストされたクラスのプライベートメンバーにアクセスすることさえできません。


プライベート(Private)メンバー

private キーワードで修飾されたメンバーは、そのメンバーが定義されているクラスまたはオブジェクトの内部でのみ可視です。同じ規則は内部クラスにも適用されます。

例

class Outer{
    class Inner{
        private def f(){
            println("f")
        }
        class InnerMost{
            f() // 正しい
        }
    }
    (new Inner).f() // 誤り
}

(new Inner).f( )アクセスが不正なのは、fInner 内で private として宣言されており、そのアクセスがクラス Inner の内部にはないためです。

しかし、InnerMost 内でのアクセスは、f問題ありません。そのアクセスはクラス Inner の内部に含まれているからです。

Java ではこの2つのアクセスが許可されています。Java は外部クラスが内部クラスのプライベートメンバーにアクセスすることを許可しているからです。


プロテクテッド(Protected)メンバー

Scala では、protected(プロテクテッド)メンバーへのアクセスは Java よりも厳格です。protected メンバーは、そのメンバーを定義したクラスのサブクラス内でのみアクセスが許可されるからです。一方 Java では、protected キーワードで修飾されたメンバーは、そのメンバーを定義したクラスのサブクラスがアクセスできることに加えて、同じパッケージ内の他のクラスもアクセスできます。

例

package p {
    class Super {
        protected def f() {println("f")}
    }
    class Sub extends Super {
        f()
    }
    class Other {
        (new Super).f() // 誤り
    }
}

上記の例では、クラス Sub による f へのアクセスは問題ありません。f は Super 内で protected として宣言されており、Sub は Super のサブクラスだからです。一方、Other による f へのアクセスは許可されません。Other は Super を継承していないからです。そして後者(Other によるアクセス)は Java では認められています。Other と Sub が同じパッケージ内にあるためです。


パブリック(Public)メンバー

Scala では、修飾子を何も指定しない場合、デフォルトで public になります。このようなメンバーはどこからでもアクセスできます。

例

class Outer {
   class Inner {
      def f() { println("f") }
      class InnerMost {
         f() // 正しい
      }
   }
   (new Inner).f() // 正しい。f() は public だから
}

パッケージプライベートアクセス(Package-Private)

Scala はパッケージプライベートアクセス修飾子もサポートしており、メンバーを特定のパッケージ内でのみアクセスできるように制限します。これは private キーワードの後にパッケージ名を続けることで実現できます。形式は次のとおりです:

private[x] 

或 

protected[x]

ここでのxは、所属するパッケージ、クラス、またはシングルトンオブジェクトを指します。

次のように書いた場合、private[x]これは次のように解釈されます:「このメンバーは、[…] 内のクラス、または […] 内のパッケージ内のクラスとそのコンパニオンオブジェクトに対してのみ可視であり、他のすべてのクラスに対しては private です」。

このテクニックは、複数のパッケージにまたがる大規模なプロジェクトで非常に役立ちます。プロジェクト内のいくつかのサブパッケージ内では可視であるが、プロジェクト外部のクライアントからは常に不可視なものを定義することができます。

例

package bobsrockets{
    package navigation{
        private[bobsrockets] class Navigator{
         protected[navigation] def useStarChart(){}
         class LegOfJourney{
             private[Navigator] val distance = 100
             }
            private[this] var speed = 200
            }
        }
        package launch{
        import navigation._
        object Vehicle{
        private[launch] val guide = new Navigator
        }
    }
}

上記の例では、クラス Navigator が private[bobsrockets] とマークされています。つまり、このクラスは bobsrockets パッケージに含まれるすべてのクラスとオブジェクトに対して可視であるということです。

例えば、Vehicle オブジェクトから Navigator へのアクセスは許可されます。Vehicle オブジェクトは launch パッケージに含まれており、launch パッケージは bobsrockets 内にあるためです。逆に、bobsrockets パッケージの外にあるすべてのコードはクラス Navigator にアクセスできません。


コンパニオンオブジェクトとコンパニオンクラス

Scala では、コンパニオンオブジェクトとコンパニオンクラスは互いのプライベートメンバーにアクセスできます。コンパニオンオブジェクトとコンパニオンクラスは、同じ名前を持ち、同じファイル内にあるクラスとオブジェクトです。

例

class CompanionExample {
  private val privateField: String = "I am private in class"
}

object CompanionExample {
  def accessPrivateField(example: CompanionExample): String = {
    example.privateField  // コンパニオンオブジェクトはクラスの private メンバーにアクセスできる
  }
}

object Main {
  def main(args: Array[String]): Unit = {
    val example = new CompanionExample
    println(CompanionExample.accessPrivateField(example))
  }
}
その他の拡張