来自:http://www.jesperdj.com/2016/01/08/scala-access-modifiers-and-qualifiers-in-detail/

Just like Java and other object-oriented programming languages, Scala has access modifiers to restrict access to members of classes, traits, objects and packages. Scala’s access modifiers are slightly different than Java’s; in this post I explain the difference. Besides modifiers, Scala also has qualifiers which allow more fine-grained access control. This is a feature that doesn’t exist in Java.

Access modifiers in Java and Scala

Java has four access levels:

Java access levels
Modifier Class Package Subclass World
public Y Y Y Y
protected Y Y Y N
no modifier Y Y N N
private Y N N N

Scala has three access levels, and they differ slightly from Java’s levels:

Scala access levels
Modifier Class Companion Subclass Package World
no modifier Y Y Y Y Y
protected Y Y Y N * N
private Y Y N N * N

*: Top-level protected and private members are accessible from inside the package.

Notable differences between Java and Scala are:

  • There is no public keyword in Scala. The default access level (when no modifier is specified) corresponds to Java’s public access level.
  • Unlike in Java, in Scala members that are protected are not accessible from other unrelated classes, traits or objects in the same package.
  • There is no direct equivalent to Java’s default access level in Scala.
  • In Scala, members that are private are accessible not only inside the class itself, but also in the class’ companion object (and vice versa: aprivate member in an object is accessible also in the object’s companion class).
  • You can make a top-level type private or protected in Scala (see below); this is not possible in Java.

In my opinion, Scala’s access levels make more sense than Java’s; the default access level in Java (package-wide access) is hardly ever used in practice, so it’s strange to have this as the default, and the fact that protected members are accessible to unrelated classes in the same package is most of the time not what you want. In Java, it’s not possible to make members accessible only to other members in the class itself and to subclasses, but not other classes in the same package.

Private top-level types

In Java, top-level classes and interfaces can only have one of two access levels: public or the default (package-scope) access level. You cannot make a top-level class or interface private or protected in Java.

In Java and also in Scala, when you make a nested class private, it means that the class is accessible only from the enclosing class. The meaning of private for a top-level class in Scala is just an extension of this idea, but now the enclosing scope is the package in which the class is defined instead of an enclosing class.

So, at first sight, it looks like a private top-level class in Scala is the same as a top-level class in Java with no access modifier; in both cases, the class is accessible only from the package that it is defined in. But this is not entirely correct. In Java, a class with no access modifier is not accessible in a nested package:

 
1
2
3
4
5
6
7
8
9
// Java example
package com.jesperdj.example;
 
// Top-level class with package-level accessibility
class Example {
    public void sayHello() {
        System.out.println("Hello World");
    }
}
 
1
2
3
4
5
6
7
8
9
10
11
12
// Nested package (under com.jesperdj.example)
package com.jesperdj.example.nested;
 
// ERROR: class Example is not accessible
import com.jesperdj.example.Example;
 
public class FromNestedPackage {
    public void method() {
        // ERROR: class Example is not accessible
        new Example().sayHello();
    }
}

In Scala, a private top-level class is also accessible from nested packages. This makes sense, since the enclosing scope of both the private class and the nested package is the same (it’s the package inside which both are defined), and private means that the class is accessible from anywhere within its enclosing scope.

 
1
2
3
4
5
6
7
// Scala example
package com.jesperdj.example
 
// Private top-level class
private class Example {
  def sayHello(): Unit = println("Hello World")
}
 
1
2
3
4
5
6
7
// Nested package (under com.jesperdj.example)
package com.jesperdj.example.nested
 
class FromNestedPackage {
  // OK: class Example is accessible because this is in the same enclosing scope
  def method(): Unit = new Example().sayHello()
}

This difference comes from the fact that the idea of nested packages do not really exist in Java, even though it seems that way. The Java compiler treats the packages com.jesperdj.example and com.jesperdj.example.nested as two unrelated packages. The fact that the second package looks like it is inside the first package doesn’t mean anything to the Java compiler.

The Scala compiler does have a notion of nested packages, which can also be seen by the fact that we didn’t have to import the classcom.jesperdj.example.Example like we would have to do in Java.

Another difference is that Scala does not allow you to let a private class escape from its enclosing scope. For example, Scala doesn’t allow you to write a public method that returns a private class. This makes sense, since the method can be called from anywhere because it’s public, but its return type is not accessible from everywhere.

 
1
2
3
4
5
6
7
8
// Scala example
package com.jesperdj.example
 
class ExampleFactory {
  // Public method which returns an instance of a private class
  // ERROR: private class Example escapes its defining scope
  def createExample(): Example = new Example
}

The Java compiler doesn’t complain when you write a public method that returns a package-scope class. Instead of complaining about the method definition, it’s going to complain at the point where you try to use the package-scope class.

 
1
2
3
4
5
6
7
8
9
// Java example
package com.jesperdj.example;
 
public class ExampleFactory {
    // OK: Public method which returns an instance of a package-scope class
    public Example createExample() {
        return new Example();
    }
}
 
1
2
3
4
5
6
7
8
9
10
11
12
// Different package
package com.jesperdj.other;
 
// ERROR: class Example is not accessible
import com.jesperdj.example.Example;
 
public class SomewhereElse {
    public void method() {
        // ERROR: class Example is not accessible
        Example example = new ExampleFactory().createExample();
    }
}

What the Scala compiler does is better; it reports the error closer to the actual source of the error. Java will let you write a library with a method that returns an inaccessible type, and you wouldn’t discover the error until somebody would try to use your library.

Protected top-level types

Even though Scala allows it, there is not much benefit to make a top-level class protected instead of private – they effectively mean the same thing.

To understand this, think about nested classes. When you make a nested class protected instead of private, it means its accessible not only in its enclosing class, but also in subclasses of its enclosing class. For top-level classes, the enclosing scope is a package instead of a class. Since there is no inheritance between packages (a package cannot extend another package, like a class can extend a superclass), protected doesn’t mean something else than private from the viewpoint of packages.

Access qualifiers

Scala’s protected and private access modifiers have an interesting feature that give you more fine-grained control. You can add a qualifier after the keywords protected and private, between square brackets. The qualifier can be one of two things: the name of an enclosing scope (package, class, trait or object), or the keyword this. These two kinds of qualifiers have different meanings.

Qualified access to an enclosing scope

With a qualifier that refers to an enclosing scope, you can widen the accessibility of a private or protected member to that scope, so that it’s not only accessible in the immediate enclosing scope, but in a scope that is one or more levels higher up. For example:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package outside {
  package inside {
    object Messages {
      // Accessible up to package 'inside'
      private[inside] val Insiders = "Hello Friends"
 
      // Accessible up to package 'outside'
      private[outside] val Outsiders = "Hello People"
    }
 
    object InsideGreeter {
      def sayHello(): Unit =
        // Can access both messages
        println(Messages.Insiders + " and " + Messages.Outsiders)
    }
  }
 
  object OutsideGreeter {
    def sayHello(): Unit =
      // Can only access the 'Outsiders' message
      println(inside.Messages.Outsiders)
  }
}

Note that the name between the square brackets must be a simple name; it cannot be a fully-qualified name (with parts separated by dots).

Qualified access to ‘this’

Normally, in both Java and Scala, when a member of a class or trait is private or protected, this means that the access is restricted to the class or trait. Access is not restricted to individual instances; an instance of a class can access the same member in other instances of the same class.

In Scala it is possible to restrict access to the same instance only by qualifying the access modifier with [this], making the member object-private or object-protected. This means that an instance of a class or trait can only access the member in the current instance, and not in other instances. For example:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Counter {
  // Normal private member variable
  private var total = 0
 
  // Object-private member variable
  private[this] var lastAdded = 0
 
  def add(n: Int): Unit = {
    total += n
    lastAdded = n
  }
 
  def copyFrom(other: Counter): Unit = {
    // OK, private member from other instance is accessible
    total = other.total
 
    // ERROR, object-private member from other instance is not accessible
    lastAdded = other.lastAdded
  }
}

Conclusion

The access modifiers in Scala are slightly different than in Java. In my opinion, they are better in Scala, for the following reasons:

  • Public is the default in Scala, which is more useful than Java’s default access level, which is rarely useful in practice in Java code.
  • protected has the same meaning in Scala as in C++; only accessible in the class itself and in subclasses. Fortunately Scala didn’t inherit Java’s weird idea, where protected members are also accessible in unrelated classes that happen to be in the same package.
  • The meaning of private top-level types in Scala makes sense.
  • When you accidentally expose a type that has restricted access from a less restricted method, Scala gives you an error at the declaration of the method (the actual source of the error) rather than at the point where you try to call the method.
  • With qualifiers, and because Scala understands nested packages, you have more fine-grained control over accessibility in Scala than in Java.
  • In Scala you can make members really really private by restricting access to only the current instance, which isn’t possible in Java.

In this post I’ve descibed Scala’s access control in detail. It’s possible to go even deeper, by looking at how the Scala compiler implements these features at the JVM-level. I suspect that the class file format that the JVM uses is really geared towards Java’s access control model, and I wonder how Scala manages to implements its own features, which are different and go beyond Java’s model, using what the class file format and the JVM offer. That will be a topic for another day.

Scala access modifiers and qualifiers in detail的更多相关文章

  1. Part 48 to 51 Talking about Access Modifiers in C#

    Part 48 Difference between Types and Type Members Part 49 Access Modifiers in C# Part 50 Internal an ...

  2. Java Access Levels(访问控制)

    Access Levels Modifier Class Package Subclass World public Y Y Y Y protected Y Y Y N no modifier Y Y ...

  3. The third column indicates whether subclasses of the class declared outside this package have access to the member.;

    总结1.modifier 改性剂 修饰符 修饰语 调节器access modifier 访问修饰符 存取权限 存取修饰词 存取修饰字官网“can”Access level modifiers dete ...

  4. Core Java Volume I — 5.1. Classes, Superclasses, and Subclasses

    5.1. Classes, Superclasses, and SubclassesLet's return to the Employee class that we discussed in th ...

  5. 【转】Basic C# OOP Concept

    This Article will explain a very simple way to understand the basic C# OOP Concept Download ShanuBas ...

  6. Visual C#每一次新版本的变化

    What's New in Visual C# .NET 2003[Visual Studio .NET 2003] What's New in Visual C# 2005 What's New i ...

  7. Java Interview Reference Guide--reference

    Part 1 http://techmytalk.com/2014/01/24/java-interview-reference-guide-part-1/ Posted on January 24, ...

  8. Unity 官方教程 学习

    Interface & Essentials Using the Unity Interface 1.Interface Overview https://unity3d.com/cn/lea ...

  9. 高级数据库及一步一步搭建versant数据库

    总的来说,高级数据库课程分为分布式数据库和面向对象数据库两块.分布式数据库介绍了分布式数据库的方方面面,包括数据库系统的设计.查询处理优化.事务管理和恢复.并发控制.可靠性.安全性与目录管理等.面向对 ...

随机推荐

  1. update select

    update中加入select 最常用的update语法是:UPDATE <table_name>SET <column_name1> = <value>, SET ...

  2. 获取当前html标签自定义属性的值

    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/ ...

  3. 团队博客作业Week1

    Study the projects done by previous student groups - View their blog site, use their software, email ...

  4. Mac上Homebrew的使用 (Homebrew 使 OS X 更完整)

    0 Homebrew是啥? “Homebrew installs the stuff you need that Apple didn’t.——Homebrew 使 OS X 更完整”. Homebr ...

  5. table中的标题行冻结的简单实现

    这里只是简单的实现,主要是用了position属性的fixed属性值,这个属性值需要高版本浏览器的支持,如果要兼容低版本的浏览器可以通过写脚本的方式实现,也可以使用UI库,有些UI库里面表格插件的标题 ...

  6. imageNamed、imageWithContentsOfFile、imageWithData

    [UIImage imageNamed:ImageName]; 1.加载图片占据的内存较大 2.相同的图片只会加载一份到内存中,如果同时使用,使用同一个对象即可 3.当对象销毁,图片对象不会随着一起销 ...

  7. decode 横竖转换 2

    select sno,nvl(to_char(sum(decode(cno,'c001',score))),'-') c001,nvl(to_char(sum(decode(cno,'c002',sc ...

  8. struts2 学习记录 过滤器 国际化

    struts2接触不是一天两天了,但是一直没有用它做什么项目,但老师确一直说它有很大的学习价值,所以还是把我学习到的东西给记录一下,记录的东西没有规律,只是给自己留个备份, struts2中最关键的是 ...

  9. 关闭 selinux 和防火墙

    1.关闭 selinux 修改 它的配置文档 /etc/selinux/conf 修改 SELINUX=disabled 或者permissive 2. 关闭 防火墙 输入命令 systemctl d ...

  10. 直接请求URL调用 axis webservices

    假设 http://127.0.0.1/services/Services?wsdl 有名称为 login 方法,且参数为 name , pwd 则,URL请求如下 http://127.0.0.1/ ...