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) {}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风吹千里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值