Scala訪問修飾符學習筆記


個人理解,有什麼理解的不對的地方,希望大家評論回覆,大家共同探討

Scala 訪問修飾符

Scala 訪問修飾符基本和Java的一樣,分別有:private,protected,public。

如果沒有指定訪問修飾符,默認情況下,Scala 對象的訪問級別都是 public。

Scala 中的 private 限定符,比 Java 更嚴格,在嵌套類情況下,外層類甚至不能訪問被嵌套類的私有成員。

私有(Private)成員

內部類和內部object的private屬性和方法對於外部類型和object不可訪問;

class Outer{
    class Inner{
    private def f(){println("f")}
    class InnerMost{
        f() // 正確
        }
    }
    (new Inner).f() //錯誤
}

(new Inner).f( ) 訪問不合法是因爲 f 在 Inner 中被聲明爲 private,而訪問不在類 Inner 之內。

但在 InnerMost 裏訪問 f 就沒有問題的,因爲這個訪問包含在 Inner 類之內。

Java中允許這兩種訪問,因爲它允許外部類訪問內部類的私有成員。

保護(Protected)成員

在 scala 中,對保護(Protected)成員的訪問比 java 更嚴格一些。因爲它只允許保護成員在定義了該成員的的類的子類中被訪問。而在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。而後者在 java 裏同樣被認可,因爲 Other 與 Sub 在同一包裏。

公共(Public)成員

Scala中,如果沒有指定任何的修飾符,則默認爲 public。這樣的成員在任何地方都可以被訪問。

class Outer {
   class Inner {
      def f() { println("f") }
      class InnerMost {
         f() // 正確
      }
   }
   (new Inner).f() // 正確因爲 f() 是 public
}

作用域保護

Scala中,訪問修飾符可以通過使用限定詞強調。格式爲:

private[x] 

或 

protected[x]

這裏的x指代某個所屬的包、類或單例對象。如果寫成private[x],讀作"這個成員除了對[…]中的類或[…]中的包中的類及它們的伴生對像可見外,對其它所有類都是private。

這種技巧在橫跨了若干包的大型項目中非常有用,它允許你定義一些在你項目的若干子包中可見但對於項目外部的客戶卻始終不可見的東西。

package bobsrocckets{
    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。

實現機制

先看一組代碼
Main.scala

object Main {

  def main(args: Array[String]):Unit={
    var p = new Person()
    p.a = "abc"
    p.testa()
    var s = new Student("stu")//g一旦賦值不可更改,類似java 的final 類型
    s.a = "bbc"
    s.testa()
  }
}

Person.scala

class Person {
  var a = "a"
  private var b = "b"
  private[this] var c = "c"
  protected[this] var d = "d"
  protected var e = "e"

  def testa(): Unit ={
    a = "a"
    b = "b"
    c = "c"
    d = "d"
    e = "e"
  }

  private def testb(): Unit ={
    a = "a"
    b = "b"
    c = "c"
    d = "d"
    e = "e"
  }

  protected def testc(): Unit ={
    a = "a"
    b = "b"
    c = "c"
    d = "d"
    e = "e"
  }

}

Student.scala

class Student(g:String)
  var a = "a"
  private var b = "b"
  private[this] var c = "c"
  protected[this] var d = "d"
  protected var e = "e"

  def testa(): Unit ={
    a = "a"
    b = "b"
    c = "c"
    d = "d"
    e = "e"
  }

  private def testb(): Unit ={
    a = "a"
    b = "b"
    c = "c"
    d = "d"
    e = "e"
  }

  protected def testc(): Unit ={
    a = "a"
    b = "b"
    c = "c"
    d = "d"
    e = "e"
  }

其中Stu和Per類唯一區別在於Stu比Per多了個(g:String)
我們來看scala實現這種訪問控制實現機制,首先就要看scala被解釋成的class字節碼是什麼樣子。

我們來看Main的字節碼
Main.class

public final class Main
{
  public static void main(String[] paramArrayOfString)
  {
    Main..MODULE$.main(paramArrayOfString);
  }
}

Main$.class

public final class Main$
{
  public static  MODULE$;
  
  static
  {
    new ();
  }
  
  public void main(String[] args)
  {
    Person p = new Person();
    p.a_$eq("abc");//p.a = "abc"scala 編譯器編程Person類的特殊函數
    p.testa();
    Student s = new Student("stu");
    s.a_$eq("bbc");//s.a = "bbc" scala 編譯器編程Person類的特殊函數
    s.testa();
  }
  
  private Main$()
  {
    MODULE$ = this;
  }
}

Person.class

public class Person
{
  private String a = "a";//var a = "a",私有屬性
  public String a()//public get 方法
  {
    return this.a;
  }
  
  public void a_$eq(String x$1)//public set方法
  {
    this.a = x$1;
  }
  private String b = "b";//private var b = "b" 明確聲明爲private
  private String b()//private get 方法
  {
    return this.b;
  }
  
  private void b_$eq(String x$1)//private set方法
  {
    this.b = x$1;
  }
  //note var a = "a" 和 private String b = "b"的差異在於
  //編譯後的set/get方法來控制變量的訪問控制
  
  
  //private[this]  屬性實際是沒有set/get方法的私有變量,
  //來實現只能內部訪問的
  private String c = "c";//private[this] var c = "c"
  
  private String d = "d";//protected[this] var d = "d"
  public String d()
  {
    return this.d;
  }
  
  public void d_$eq(String x$1)
  {
    this.d = x$1;
  }
  
  
  private String e = "e";//protected var e = "e"
  public String e()
  {
    return this.e;
  }
  
  public void e_$eq(String x$1)
  {
    this.e = x$1;
  }
  //這裏我有一個疑惑,目前沒有找到相關文檔。
  //我這裏做一個大膽的猜測,這裏的scala代碼被編譯成public方法,但是
  //你在scala源碼中,非子類是不能訪問d和e變量,所以我猜測,
  //這個報錯是編譯器報錯,在運行時是可以訪問這個變量的。
  
  public void testa()
  {
    a_$eq("a");
    b_$eq("b");
    this.c = "c";
    d_$eq("d");
    e_$eq("e");
  }
  
  private void testb()
  {
    a_$eq("a");
    b_$eq("b");
    this.c = "c";
    d_$eq("d");
    e_$eq("e");
  }
  
  public void testc()
  {
    a_$eq("a");//a = "a"
    b_$eq("b");//b = "b"
    this.c = "c";//c = "c"
    d_$eq("d");//d = "d"
    e_$eq("e");//e = "e"
  }
}

Student.class

public class Student
{
  public String a()
  {
    return this.a;
  }
  
  public void a_$eq(String x$1)
  {
    this.a = x$1;
  }
  
  private String a = "a";
  
  private String b()
  {
    return this.b;
  }
  
  private void b_$eq(String x$1)
  {
    this.b = x$1;
  }
  
  private String b = "b";
  private String c = "c";
  
  public String d()
  {
    return this.d;
  }
  
  public void d_$eq(String x$1)
  {
    this.d = x$1;
  }
  
  private String d = "d";
  
  public String e()
  {
    return this.e;
  }
  
  public void e_$eq(String x$1)
  {
    this.e = x$1;
  }
  
  private String e = "e";
  
  public void testa()
  {
    a_$eq("a");
    b_$eq("b");
    this.c = "c";
    d_$eq("d");
    e_$eq("e");
  }
  
  private void testb()
  {
    a_$eq("a");
    b_$eq("b");
    this.c = "c";
    d_$eq("d");
    e_$eq("e");
  }
  
  public void testc()
  {
    a_$eq("a");
    b_$eq("b");
    this.c = "c";
    d_$eq("d");
    e_$eq("e");
  }
  private final String g;
  //這是Student與Person最大的區別,Student有一個構造函數
  public Student(String g) {}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章