Scala’s access modifier is different from Java:
Java defaults to package internal visibility if you don’t specify any access modifier. Scala, on the other hand, defaults to public.
Java provides an all-or-nothing proposition. Either it’s visible to all classes in the current package or it’s not visible to any. Scala gives fine-grained control over visibility.
Java’s protected is generous. It includes derived classes in any package plus any class in the current package. Scala’s protected is akin to C++ and C#—only derived classes can access it. However, you can also ask Scala for quite a liberal and flexible interpretation of protected.
Finally, Java encapsulation is at the class level. You can access the private fields and methods of any object of a class from within its instance method. This is the default in Scala as well; however, you can customize it to access only within the current instance’s methods, similar to what Ruby provides.
Let’s explore these variations from Java using some examples.
By default, Scala treats classes, fields, and methods as public if you don’t use an access modifier. If you want to make a member private or protected, simply mark it with the respective keyword like this:
FromJavaToScala/Access.scala | |
| class Microwave { |
| def start() = println("started") |
| def stop() = println("stopped") |
| private def turnTable() = println("turning table") |
| } |
| val microwave = new Microwave |
| microwave.start() // OK |
In this code, by default the methods start and stop are public. We can access those two methods on any instance of Microwave. On the other hand, we’ve defined turnTable explicitly as private. We can’t access that method from outside the class. If we try, as in the previous example, we will get this error:
| Access.scala:9: error: method turnTable in class Microwave cannot be |
| accessed in this.Microwave |
| microwave.turnTable() //ERROR |
| ^ |
| one error found |
Leave out access modifiers for public fields and methods. For other members, make the access as restrictive as desired with an explicit access modifier.
In Scala, protected makes the decorated members visible to the class and its derived classes only. Other classes that belong to the same package, of the defining class, can’t access these members. Furthermore, the derived class can access the protected members only on its own type. Let’s examine these with an example:
FromJavaToScala/Protected.scala | |
Line 1 | package automobiles |
- | |
- | class Vehicle { |
- | protected def checkEngine() {} |
5 | } |
- | class Car extends Vehicle { |
- | def start() { checkEngine() /*OK*/ } |
- | def tow(car: Car) { |
- | car.checkEngine() //OK |
10 | } |
- | def tow(vehicle: Vehicle) { |
- | vehicle.checkEngine() //ERROR |
- | } |
- | } |
15 | class GasStation { |
- | def fillGas(vehicle: Vehicle) { |
- | vehicle.checkEngine() //ERROR |
- | } |
- | } |
By compiling the code we can see, in the compiler error message, these access controls taking effect:
| Protected.scala:12: error: method checkEngine in class Vehicle cannot be |
| accessed in automobiles.Vehicle |
| Access to protected method checkEngine not permitted because |
| prefix type automobiles.Vehicle does not conform to |
| class Car in package automobiles where the access take place |
| vehicle.checkEngine() //ERROR |
| ^ |
| Protected.scala:17: error: method checkEngine in class Vehicle cannot be |
| accessed in automobiles.Vehicle |
| Access to protected method checkEngine not permitted because |
| enclosing class GasStation in package automobiles is not a subclass of |
| class Vehicle in package automobiles where target is defined |
| vehicle.checkEngine() //ERROR |
| ^ |
| two errors found |
In the previous code, checkEngine of Vehicle is protected and is accessible from any instance method of Vehicle. We can access that method from within an instance method, like start, of the derived class Car. We can also access it on an instance of Car from within an instance method, like tow, of Car. However, we can’t access that method on an instance of Vehicle from within Car and also from within another arbitrary class, like GasStation, even if it belongs to the same package as Vehicle. This behavior is different from how Java treats protected access. Scala is a lot more stringent about access to protected members.
On one hand, Scala is more restrictive than Java in how it treats the protected modifier. On the other hand, it gives a far greater flexibility and also fine-grained control over setting access visibility.
You can specify additional parameters for private and protected modifiers. So, instead of simply decorating a member with private, you can decorate it as private[AccessQualifier], where AccessQualifier may be any enclosing class name, an enclosing package name, or this—meaning instance-only visibility.
The qualifier in the access modifier may tell Scala to treat the member as private for all classes except in these cases:
If no AccessQualifier is given—this is the default—then the member is accessible only from the current class and its companion object (we’ll look at companion objects in Chapter 4, Working with Objects).
If the AccessQualifier is a class name, then the member is accessible from the class, its companion objects, plus the class and the companion objects of the enclosing class whose name is given as the AccessQualifier.
If the AccessQualifier is an enclosing package name, then the member is accessible from the class, its companion object, plus within any class nested under the mentioned package.
If the AccessQualifier is this, then the access to the member is restricted to the instance and is not visible to other instances of the same class—this is the most restrictive of all the options.
That’s quite a few combinations that can set heads spinning. An example of the fine-grained access control can help make things clear:
FromJavaToScala/FineGrainedAccessControl.scala | |
Line 1 | package society { |
- | |
- | package professional { |
- | class Executive { |
5 | private[professional] var workDetails = null |
- | private[society] var friends = null |
- | private[this] var secrets = null |
- | |
- | def help(another : Executive) = { |
10 | println(another.workDetails) |
- | println(secrets) |
- | println(another.secrets) //ERROR |
- | } |
- | } |
15 | |
- | class Assistant { |
- | def assist(anExec: Executive) = { |
- | println(anExec.workDetails) |
- | println(anExec.friends) |
20 | } |
- | } |
- | } |
- | |
- | package social { |
25 | class Acquaintance { |
- | def socialize(person: professional.Executive) { |
- | println(person.friends) |
- | println(person.workDetails) // ERROR |
- | } |
30 | } |
- | } |
- | } |
Compiling the code produces the following errors:
| FineGrainedAccessControl.scala:12: error: value secrets is not a member of |
| society.professional.Executive |
| println(another.secrets) //ERROR |
| ^ |
| FineGrainedAccessControl.scala:28: error: variable workDetails in class |
| Executive cannot be accessed in society.professional.Executive |
| println(person.workDetails) // ERROR |
| ^ |
| two errors found |
The example illustrates quite a few Scala nuances. In Scala we can define nested packages, akin to C++ and C# nested namespaces. We can either follow the Java style to define packages—using dots, as in package society.professional;—or use the C++ or C# nested namespace style. If we decide to place multiple small classes belonging to a hierarchy of packages all in one file—again a departure from Java—the latter style is convenient.
In the previous code, we gave visibility for Executive’s private field workDetails to any class within the enclosing package professional. Scala thus permits access to this field from a method of the class Assistant, which is in that package. However, the class Acquaintance from another package can’t touch the field.
For the private field friends, on the other hand, we gave access to any class within the enclosing package society. This gives permission to access the field friends from the class Acquaintance, which is located in a subpackage of society.
The default visibility of private is class level—from an instance method of a class we can access the members decorated as private on any instance of the same class. However, Scala gives a finer control over private and protected with the this qualifier. For instance, in the previous example, since secrets is decorated private[this], instance methods can access this field only on implicit instances, that is, on this instance—that field can’t be accessed on other instances. That’s the reason why we can access secrets but not another.secrets from within the help instance method. Likewise, a field decorated with protected[this] is accessible from within an instance method of a derived class but only on the current instance.
3.137.195.211