In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-23 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
This article mainly explains "what is the method of Scala object-oriented programming". The content of the article is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought to study and learn "what is the method of Scala object-oriented programming".
Scala basic object-oriented programming
Like Java, Python, Ruby, Smalltalk and other similar languages, Scala is an object-oriented language. If you come from the world of Java, you will find some significant improvements to the limitations of the Java object model.
We assume that you have previous experience in object-oriented programming (OOP), so we will not discuss the most basic principles, although there are some common terms and concepts mentioned in the glossary.
The basis of classes and objects
Let's review the terminology of Scala OOP.
Be careful
We saw earlier that Scala has the concept of declaring objects, and we will be in "classes and objects: where is the state?" Chapter to discuss them. We will use the term instance to refer to an instance of a class, which means an object or instance of a class, to avoid confusion between the two.
Classes can be declared with the keyword class. We will see later that we can also add some other keywords, such as using final to prevent the creation of an inherited class, and using abstract to indicate that the class cannot be instantiated, usually because it contains or inherits member declarations that are not specifically defined.
An instance can refer to itself with the this keyword, just like Java and its similar languages.
Following the convention of Scala, we use the term method to refer to the function of the instance (function). Some object-oriented languages use the term member function (member function). The method definition starts with the def keyword.
Like Java, but unlike Ruby,Python, Scala allows you to overload methods. Two or more methods can have the same name, as long as their full signatures are unique. The signature contains the type name, the parameter list and its type, and the return value of the method.
However, there is an exception caused by type elimination, which is a feature of JVM but is utilized by Scala on JVM and .NET platforms to minimize compatibility problems. Suppose the two methods are otherwise the same, except that one accepts the list [string] parameter and the other accepts the list [Int] parameter, as shown below.
/ / code-examples/BasicOOP/type-erasure-wont-compile.scala / / WON'T COMPILE object Foo {def bar (list: List [String]) = list.toString def bar (list: List [Int]) = list.size.toString}
You will get a compilation error at the second method because both methods have the same signature after the type is eliminated.
Warning
The Scala interpreter will let you enter these two methods. It simply abandoned the first version. However, if you try to load the above example with the: load file command, you will get the same error.
Again by convention, we use the term field (field) to refer to the variables of the instance. Other languages often use the term attribute, such as Ruby. Note that the state of an instance is the union of values rendered by the fields of that instance.
As we discussed in the "variable declarations" section of "fewer words and more things in the Scala programming Guide", read-only ("value") fields are declared with the val keyword, and readable fields are declared with the var keyword.
Scala also allows types to be declared in classes, as we saw in the "Abstract types and Parametric types" chapter in "fewer words and more things in Scala programming guides."
We generally use the term member to refer to a field, method, or type. Note that field and method members (except for type members) share the same namespace, unlike Java. We will discuss this more in the section entitled "when methods and field accessors are indistinguishable: the principle of unique access" in Scala Advanced object-oriented programming.
Finally, an instance of the reference type can be created with the new keyword, just like Java,C#. Note that you don't have to write parentheses when using the default constructor (for example, a constructor with no arguments). In some cases, the literal value can be used instead of new. For example, val name = "Programming Scala" is equivalent to val name = new String ("Programming Scala").
Instances of value types, such as Int,Double, and so on, corresponding to metatypes in languages such as Java, are always created with literal values. For example, 1BI 3.14 and so on. In fact, these types do not have public constructors, so expressions such as val I = new Int (1) cannot be compiled.
We will discuss the difference between reference types and value types in the "Scala type structure" section.
Parent class
Scala supports single inheritance, not multiple inheritance. A child (or inherited) class can have only one parent class (base class). The only exception is the root in the Scala class hierarchy, Any, which has no parent class.
We have seen several examples of parent and subclasses. Here is a snippet of the first example we saw in the "Abstract types and Parametric types" chapter in "fewer words and more things in the Scala programming Guide."
/ / code-examples/TypeLessDoMore/abstract-types-script.scala import java.io._ abstract class BulkReader {/ /...} class StringBulkReader (val source: String) extends BulkReader {/ /...} class FileBulkReader (val source: File) extends BulkReader {/ /...}
As in Java, the keyword extends indicates the parent class, which in this case is BulkReader. In Scala, extends is also used when a class inherits a trait as a parent (even when it mixes other traits with the with keyword). Also, extends is used when one trait is the successor to another trait or class. Yes, traits can inherit from its own class.
If you do not inherit any parent class, the default parent is a direct subclass of AnyRef,Any. We will discuss the difference between Any and AnyRef in the section "Scala type hierarchy". )
Scala constructor
Scala can distinguish between the primary constructor and 0 or more auxiliary constructors. In Scala, the entire body of the class is the main constructor. Any parameters required by the constructor are listed after the class name. We have seen many examples, such as the ButtonWithCallbacks example we used in Chapter 4-Traits.
/ / code-examples/Traits/ui/button-callbacks.scala package ui class ButtonWithCallbacks (val label: String, val clickedCallbacks: List [() = > Unit]) extends Widget {require (clickedCallbacks! = null, "Callback list can't be null!") Def this (label: String, clickedCallback: () = > Unit) = this (label, List (clickedCallback)) def this (label: String) = {this (label, Nil) println ("Warning: button has no click callbacks!")} def click () = {/ /. Logic to give the appearance of clicking a physical button... ClickedCallbacks.foreach (f = > f ())}}
The class ButtonWithCallbacks represents a button on the graphical user interface. It has a label and a list of callback functions that are called when the button is clicked. Each callback function takes no arguments and returns Unit. The method click iterates through the list of callback functions and calls them one by one.
ButtonWithCallbacks defines three constructors. The main constructor, the subject of the class, has an argument list to accept a list of label strings and callback functions. Because each parameter is declared as val, the compiler generates a private field for each parameter (using a different internal name) and a public read method with the same name as the parameter. "private" and "public" mean the same here as in most object-oriented languages. We will discuss the different visibility rules and the keywords that control them in the visibility rules section below.
If the parameter has a var keyword, a public write method is automatically generated, and the name is the parameter name with an underscore equal sign (_ =). For example, if label is declared as var, the corresponding write method is label_=, and it accepts a string as a parameter.
Sometimes you may not want to generate these accessor methods automatically. In other words, you want the field to be private. Add the private keyword before val or var and the accessor method will not be generated. (see the "visibility rules" section for more details. )
Be careful
For Java programmers, Scala does not follow the s [JavaBeanSpec] convention-field reading and writing methods correspond to the prefixes of get and set, respectively, followed by the first uppercase field name. We will see the reason when the unique access principle is discussed in the section "when methods and field accessors are indistinguishable: the principle of unique access." However, you can get JavaBeans-style accessors through scala.reflect.BeanProperty when needed, which we will discuss in the "JavaBean attributes" section of Chapter 14-Scala tools, Libraries, and IDE support.
When an instance of the class is created, the field corresponding to each parameter is automatically initialized by the parameter. No logical constructor is required to initialize these fields, unlike many object-oriented languages.
The first instruction of the body of the ButtonWithCallbacks class (in other words, the constructor) is a test that ensures that the argument list passed in to the constructor is a non-empty list. (however, it does allow an empty Nil list. It uses the convenient require function, which is automatically imported into the current scope (as we will discuss in the "predefined objects" section of Chapter 7-Scala object system). If the list is null, require throws an exception. The require function and its corresponding assumptions are useful for designing contractual programs, which we will discuss in the chapter "Building better designs by contract" in Chapter 13, Application Design.
Here is part of the complete Specification (specification) of ButtonWithCallbacks, which demonstrates the role of the require instruction.
/ / code-examples/Traits/ui/button-callbacks-spec.scala package ui import org.specs._ object ButtonWithCallbacksSpec extends Specification {"A ButtonWithCallbacks" should {/ /... "not be constructable with a null callback list" in {val nullList:List [() = > Unit] = null val errorMessage = "requirement failed: Callback list can't be null!" (new ButtonWithCallbacks ("button1", nullList)) must throwA (new IllegalArgumentException (errorMessage))}}
Scala even makes it difficult to pass null as a second argument to the constructor; it no longer does type checking at compile time. However, you can assign null to a value as above. If we don't have must throwA (...) Clause, we will see the following exception thrown.
Java.lang.IllegalArgumentException: requirement failed: Callback list can't be null! At scala.Predef$.require (Predef.scala:112) at ui.ButtonWithCallbacks. (button-callbacks.scala:7).
ButtonWithCallbacks defines two user-friendly auxiliary constructors. The first auxiliary constructor accepts a label and a separate callback function. It calls the main constructor and passes it a label and a new list containing the callback function.
The second auxiliary constructor accepts only one label. It calls the main constructor and passes in Nil (Nil represents an empty List object). The constructor then prints a warning message indicating that there is no callback function, because the list is immutable, so we have no opportunity to replace the existing callback function list with a new value.
To avoid infinite recursion, Scala requires each auxiliary constructor to call the constructor [ScalaSpec2009] defined before it. The constructor being called can be another auxiliary constructor or a primary constructor, and it must appear in the first sentence of the body of the auxiliary constructor. Additional procedures can occur after this call, such as printing a warning message in our example.
Be careful
Because all auxiliary constructors eventually call the main constructor, logical checks and other initialization work in its body are performed when all instances are created.
Scala's constraints on constructors have some benefits.
Eliminate repetition
Because the auxiliary constructor invokes the primary constructor, the potential repetitive construction logic is greatly eliminated.
Reduction in the volume of code
As shown in the example, when one or more main constructor parameters are declared as val or var,Scala, a field is automatically generated, the appropriate access methods (unless they are defined as private, private), and the initialization logic when the instance is created.
However, there is at least one drawback.
Lack of elasticity
Sometimes it is not convenient to force all constructors to use the same constructor body. However, we find that such cases are very rare. In this case, it may be because the class is responsible for too much and should be refactored into smaller classes.
Call the parent class constructor
The main constructor of a subclass must call a constructor of the parent class, whether it is a primary constructor or an auxiliary constructor. In the following example, the class RadioButtonWithCallbacks inherits ButtonWithCallbacks and calls the main constructor of ButtonWithCallbacks. The "Radio" button can be set to on or off.
/ / code-examples/BasicOOP/ui/radio-button-callbacks.scala package ui/ * Button with two states, on or off, like an old-style, * channel-selection button on a radio. / class RadioButtonWithCallbacks (var on: Boolean, label: String, clickedCallbacks: List [() = > Unit]) extends ButtonWithCallbacks (label, clickedCallbacks) {def this (on: Boolean, label: String, clickedCallback: () = > Unit) = this (on, label, List (clickedCallback) def this (on: Boolean, label: String) = this (on, label, Nil)}
The main constructor of RadioButtonWithCallbacks takes three arguments, a switch state (true or false), a label, and a callback function sample table. It passes the list of tags and callback functions to the parent class ButtonWithCallbacks. The switch state parameter (on) is declared as var, so it is variable. On is also a private property of each radio button. To be consistent with the parent class, RadioButtonWithCallbacks also defines two auxiliary constructors. Note that they must call a previously defined constructor, as before. They cannot directly call the constructor of ButtonWithCallbacks. Declaring these constructors for all classes may be tedious, but the techniques we explored in Chapter 4-Traits can help us reduce such repetition.
Be careful
Although the super keyword, like Java, is often used to call overridden methods, it cannot be used as a constructor to call a parent class.
Nested class
Scala, like many object-oriented languages, allows you to nest declaration classes. Suppose we want all parts to have a series of properties. These properties can be size, color, visibility, and so on. We can use a simple map to hold these properties, but we assume that we also want to be able to control access to these properties and do something else when they change.
The following example shows how we can use the features we learned from the "mixed Traits" chapter in Chapter 4-Traits to extend our original Widget example.
/ / code-examples/BasicOOP/ui/widget.scala package ui abstract class Widget {class Properties {import scala.collection.immutable.HashMap private var values: Map [String, Any] = new HashMap def size = values.size def get (key: String) = values.get (key) def update (key: String, value: Any) = {/ / Do some preprocessing, e.g., filtering. Valuesvalues = values.update (key, value) / / Do some postprocessing. }} val properties = new Properties}
We added a Properties class that contains a private, mutable HashMap (the HashMap itself is immutable) reference. We also added three public methods to get the size (for example, the number of attributes defined), get the elements in map, and update the corresponding elements in map, and so on. We may need to do more work on the update method, which has been annotated.
Be careful
As you can see from the example above, Scala allows you to define another in one class, or become "nested". A nested class is useful when you have enough functionality to merge into a class that will only be used by the outer class.
So far, we have learned how to declare a class, how to initialize them, and some of the basics of inheritance. In the next section, we will discuss visibility rules within classes and objects.
Visibility Rul
Be careful
For convenience, we will use the general term "type" to refer to classes and Trait, as well as member types. Unless otherwise stated, we will include these definitions when using the general term "member".
Most object-oriented languages have structures that control type or type member visibility (scope) declarations. These structures support object-oriented encapsulation, that is, essentially only the common abstraction of the class or Trait is exposed, and the internal implementation is hidden under the horizon.
You will want to use public visibility wherever users of your classes and objects want to see and use them. Keep in mind, however, that the collection of publicly visible members constitutes the abstract interface exposed by the type, including the name of the type.
The traditional wisdom of the object-oriented design world is that fields should be private or protected. If there is an access requirement, it should also be done by method, rather than making everything accessible by default. The uniform access principle (see section "when access methods and fields are indistinguishable: unified access principles") boils down to the fact that we can give the user public field access semantics through direct access to methods or fields, as long as it is appropriate for the task.
Prompt
The art of good object-oriented design is to define the smallest, clear, cohesive layer of common abstraction.
Types have two kinds of "users": inheriting types, and code that uses instances of types. Inheriting types usually require more access to members of the parent type than instance users.
The visibility rules of Scala are similar to Java, but tend to be more uniform and flexible. For example, in Java, if an inner class has a private member, the external class that contains it is visible. In Scala, it is not visible, but Scala provides another way to declare that it is visible to the external class that contains it.
Like Java,C#, keywords that modify visibility, such as private and protected, appear at the beginning of the declaration. You will find them before the class,trait keyword, before val or var, and before the def of the method.
Be careful
You can also use a visibility modifier before the main constructor of the class. If so, put it after the type name and type parameters and before the parameter list. Like this:
Class Restricted [+ A] private (name: String) {.}
Table 5.1, "visible domains" summarizes the scope of visibility.
Table 5.1. visible domain name keyword description public no public members are visible everywhere, across all boundaries protectedprotectedprotected members are visible to the types, inherited types, and nested types that define it, and protected types are only visible in the same package, subpackage. The privateprivateprivate member is visible to the types and nested types that define it, and the private type is visible only in the same package. Scoped protected [scoped] visibility is limited to the domain scoped, which can be a package, a type, or a this (this is the instance for a member, and the package for a type that exists. See the text below for more information. Scoped privateprivatescoped is the same as scoped protected, except when inheriting. (will be discussed below)
Let's take a closer look at these visibility options. For simplicity, we will use fields as examples of members. Method, the behavior of the type declaration is consistent with the field.
Be careful
Unfortunately, you can't make any visibility modifications to the package. Therefore, a package is always public, even if it does not contain any public types.
Public visibility
Any declaration without an explicit visibility keyword is "public", meaning it is visible everywhere. There is no public keyword in Scala. This is contrary to Java, where the default behavior of Java is to default to public visibility (that is, package private-"package private") only in the current package. Other object-oriented languages, such as Ruby, are also default public visibility.
/ / code-examples/BasicOOP/scoping/public.scala package scopeA {class PublicClass1 {val publicField = 1 class Nested {val nestedField = 1} val nested = new Nested} class PublicClass2 extends PublicClass1 {val field2 = publicField + 1 val nField2 = new Nested (). NestedField}} package scopeB {class PublicClass1B extends scopeA.PublicClass1 class UsingClass (val publicClass: scopeA.PublicClass1) {def method = "UsingClass : "+" field: "+ publicClass.publicField +" nested field: "+ publicClass.nested.nestedField}}
You can compile this file with scalac, and you should not encounter compilation errors.
Any member of these packages and classes is public. The idea is that scopeB.UsingClass can access scopeA.PublicClass1 and its members, including instances of nested classes and its public field.
Protected visibility
Protected visibility provides some benefits for implementing inherited types because it requires some more access to its parent type. Any member declared with the protected keyword is visible only to the type that defines it, including its instance and any inherited types. When applied to a type, protected restricts its visibility to the package that contains it.
By contrast, Java makes protected members visible to the entire package. Scala uses scoped (regional) private and protected to control this situation.
/ / code-examples/BasicOOP/scoping/protected-wont-compile.scala / / WON'T COMPILE package scopeA {class ProtectedClass1 (protected val protectedField1: Int) {protected val protectedField2 = 1 def equalFields (other: ProtectedClass1) = (protectedField1 = = other.protectedField1) & & (protectedField1 = = other.protectedField1) & & (nested = = other.nested) class Nested {protected val nestedField = 1} protected val Nested = new Nested} class ProtectedClass2 extends ProtectedClass1 (1) {val field1 = protectedField1 val field2 = protectedField2 val nField = new Nested (). NestedField / / ERROR} class ProtectedClass3 {val protectedClass1 = new ProtectedClass1 (1) val protectedField1 = protectedClass1.protectedField1 / / ERROR val protectedField2 = protectedClass1.protectedField2 / / ERROR val protectedNField = protectedClass1.nested.nestedField / / ERROR} protected class ProtectedClass4 class ProtectedClass5 extends ProtectedClass4 protected class ProtectedClass6 extends ProtectedClass4 } package scopeB {class ProtectedClass4B extends scopeA.ProtectedClass4 / / ERROR}
When you compile this file with scalac, you will get the following output. (for typesetting, the file name before the N: line number has been removed. )
16: error: value nestedField cannot be accessed in ProtectedClass2.this.Nested val nField = new Nested () .nestedField ^ 20: error: value protectedField1 cannot be accessed in scopeA.ProtectedClass1 val protectedField1 = protectedClass1.protectedField1 ^ 21: error: value protectedField2 cannot be accessed in scopeA.ProtectedClass1 val protectedField2 = protectedClass1.protectedField2 ^ 22: error: value nested cannot be accessed in scopeA.ProtectedClass1 val protectedNField = protectedClass1.nested.nestedField ^ 32: error: class ProtectedClass4 cannot be accessed in package scopeA class ProtectedClass4B extends scopeA.ProtectedClass4 ^ 5 errors found
The / / ERROR comment in the list identifies lines that cannot be parsed.
ProtectedClass2 can access protected members of ProtectedClass1 because they are inheritance relationships. However, it cannot access the protected nestedField field of protectedClass1.nested. Also, ProtectedClass3 cannot access the protected members of the ProtectedClass1 instance it uses.
Finally, because ProtectedClass4 is declared as protected, it is not visible to the scopeB package.
Private visibility
Private visibility completely hides the details of the implementation, even for the implementation of inherited classes. Any member declared with the private keyword is visible only to the type that defines it, including its instance. When applied to a type, private restricts its visibility to the package that contains it.
/ / code-examples/BasicOOP/scoping/private-wont-compile.scala / / WON'T COMPILE package scopeA {class PrivateClass1 (privateval privateField1: Int) {privateval privateField2 = 1 def equalFields (other: PrivateClass1) = (privateField1 = = other.privateField1) & & (privateField2 = = other.privateField2) & & (nested = = other.nested) class Nested {privateval nestedField = 1} privateval nested = new Nested } class PrivateClass2 extends PrivateClass1 (1) {val field1 = privateField1 / / ERROR val field2 = privateField2 / / ERROR val nField = new Nested (). NestedField / / ERROR} class PrivateClass3 {val privateClass1 = new PrivateClass1 (1) val privateField1 = privateClass1.privateField1 / / ERROR val privateField2 = privateClass1.privateField2 / / ERROR val privateNField = privateClass1.nested.nestedField / / ERROR} private class PrivateClass4 class PrivateClass5 extends PrivateClass4 / / ERROR protected class PrivateClass6 extends PrivateClass4 / / ERROR private class PrivateClass7 extends PrivateClass4} package scopeB {class PrivateClass4B extends scopeA.PrivateClass4 / / ERROR}
Compiling this file produces the following output.
14: error: not found: value privateField1 val field1 = privateField1 ^ 15: error: not found: value privateField2 val field2 = privateField2 ^ 16: error: value nestedField cannot be accessed in PrivateClass2.this.Nested val nField = new Nested (). NestedField ^ 20: error: value privateField1 cannot be accessed in scopeA.PrivateClass1 val PrivateField1 = privateClass1.privateField1 ^ 21: error: value privateField2 cannot be accessed in scopeA.PrivateClass1 val privateField2 = privateClass1.privateField2 ^ 22: error: value nested cannot be accessed in scopeA.PrivateClass1 val privateNField = privateClass1.nested.nestedField ^ 27 : error: private class PrivateClass4 escapes its defining scope as part of type scopeA.PrivateClass4 class PrivateClass5 extends PrivateClass4 ^ 28: error: private class PrivateClass4 escapes its defining scope as part of type scopeA.PrivateClass4 protected class PrivateClass6 extends PrivateClass4 ^ 33: error: class PrivateClass4 cannot be accessed in package scopeA class PrivateClass4B extends scopeA.PrivateClass4 ^ 9 errors found
At this time, PrivateClass2 cannot access the private members of its parent class PrivateClass1. As the error message indicates, they are completely invisible to subclasses. Nor can they access the private field of a nested class.
As with the example of protected access, PrivateClass3 cannot access the private members of the PrivateClass1 instance it uses. Note, however, that the equalFields method can access private members of other instances.
The declaration of PrivateClass5 and PrivateClass6 failed because, if allowed, they allowed PrivateClass4 to "jump out of its domain". However, PrivateClass7's declaration was successful because it was also defined as private. Curiously, we correctly defined a public class that inherits from the protected class in our previous example.
Finally, like the protected type declaration, the private type cannot be inherited outside of the package that contains it.
Local Private and Protected visibility
Scala allows you to fine-tune the scope of visibility using scoped private and protected visibility declarations. Note that the use of proviate or protected in local declarations is interchangeable because they are all the same except for the visibility applied to inherited members.
Prompt
Although choosing either one will have the same effect in most cases, using private in your code is still more common than protected. In Scala's core library, this Billy is about 5:1.
Let's start with the only difference between scoped private and scoped protected and see how members work under the inheritance mechanism when they have these local declarations.
/ / code-examples/BasicOOP/scoping/scope-inheritance-wont-compile.scala / / WON'T COMPILE package scopeA {class Class1 {private [scopeA] val scopeA_privateField = 1 protected [scopeA] val scopeA_protectedField = 2 private [Class1] val class1_privateField = 3 protected [Class1] val class1_protectedField = 4 private [this] val this_privateField = 5 protected [this] val this_protectedField = 6} class Class2 extends Class1 {val field1 = scopeA_privateField val field2 = scopeA_protectedField val field3 = class1_privateField / / ERROR val field4 = class1_protectedField val field5 = this_privateField / / ERROR val field6 = this_protectedField}} package scopeB {class Class2B extends scopeA.Class1 {val field1 / / ERROR val field2 = scopeA_protectedField val field3 = class1_privateField / / ERROR val field4 = class1 _ protectedField val field5 = this_privateField / / ERROR val field6 = this_protectedField}}
Compiling this file produces the following output.
17: error: not found: value class1_privateField val field3 = class1_privateField / / ERROR ^ 19: error: not found: value this_privateField val field5 = this_privateField / / ERROR ^ 26: error: not found: value scopeA_privateField val field1 = scopeA_privateField / / ERROR ^ 28: error: not found: value class1_privateField val field3 = class1 _ privateField / / ERROR ^ 30: error: not found: value this_privateField val field5 = this_privateField / / ERROR ^ 5 errors found
The first two errors in Class2 state that an inherited class within the same package cannot refer to the parent class or the scoped private member of the this, but it can refer to the private member of the package (or type) that contains Class1 and Class2.
By contrast, for inherited classes other than package, it cannot access any of the scoped private members of Class1.
However, all scoped protected members are visible to both inherited classes.
We will use scoped private declarations in the rest of the examples and discussions, because scoped private is more common than scoped protected in the Scala library, and the previous inheritance situation is not a factor.
First, let's start with the strictest visibility, private [this], which also works on type members.
/ / code-examples/BasicOOP/scoping/private-this-wont-compile.scala / / WON'T COMPILE package scopeA {class PrivateClass1 (private [this] val privateField1: Int) {private [this] val privateField2 = 1 def equalFields (other: PrivateClass1) = (privateField1 = = other.privateField1) & & / / ERROR (privateField2 = = other.privateField2) & & (nested = = other.nested) class Nested {private [this] val nestedField = 1} private [this] val nested = new Nested} class PrivateClass2 extends PrivateClass1 (1) {val field1 = privateField1 / / ERROR val field2 = privateField2 / / ERROR val nField = new Nested (). NestedField / / ERROR} class PrivateClass3 {val privateClass1 = new PrivateClass1 (1) val privateField1 = privateClass1.privateField1 / / ERROR val privateField2 = privateClass1.privateField2 / / ERROR val privateNField = privateClass1.nested.nestedField / / ERROR}}
Compiling this file produces the following output.
5: error: value privateField1 is not a member of scopeA.PrivateClass1 (privateField1 = = other.privateField1) & & ^ 14: error: not found: value privateField1 val field1 = privateField1 ^ 15: error: not found: value privateField2 val field2 = privateField2 ^ 16: error: value nestedField is not a member of PrivateClass2. This.Nested val nField = new Nested () .nestedField ^ 20: error: value privateField1 is not a member of scopeA.PrivateClass1 val privateField1 = privateClass1.privateField1 ^ 21: error: value privateField2 is not a member of scopeA.PrivateClass1 val privateField2 = privateClass1.privateField2 ^ 22: error: value nested is not a member of scopeA.PrivateClass1 val privateNField = privateClass1.nested.nestedField ^ 7 errors found
Be careful
Lines 6 to 8 cannot be parsed. Because they are part of the expression at the beginning of line 5, the compiler stops after the first error.
These private[ this] members are visible only to members within the same instance. Different instances of the same class cannot access each other's private [this] members, so the equalFields method cannot be parsed.
Otherwise, the visibility of class members is the same as that of private without domain qualifiers.
When declaring a type with private [this], the use of this is effectively bound to the package that contains it, as shown here.
/ / code-examples/BasicOOP/scoping/private-this-pkg-wont-compile.scala / / WON'T COMPILE package scopeA {private [this] class PrivateClass1 package scopeA2 {private [this] class PrivateClass2} class PrivateClass3 extends PrivateClass1 / / ERROR protected class PrivateClass4 extends PrivateClass1 / / ERROR private class PrivateClass5 extends PrivateClass1 private [this] class PrivateClass6 extends PrivateClass1 private [this] class PrivateClass7 extends scopeA2.PrivateClass2 / / ERROR} package scopeB {class PrivateClass1B extends scopeA.PrivateClass1 / / ERROR}
Compiling this file produces the following output.
Error: private class PrivateClass1 escapes its defining scope as part of type scopeA.PrivateClass1 class PrivateClass3 extends PrivateClass1 ^ 9: error: private class PrivateClass1 escapes its defining scope as part of type scopeA.PrivateClass1 protected class PrivateClass4 extends PrivateClass1 ^ 13: error: type PrivateClass2 is not a member of package scopeA.scopeA2 private [this] class PrivateClass7 extends scopeA2.PrivateClass2 ^ 17: error: type PrivateClass1 is not a member of package scopeA class PrivateClass1B extends scopeA.PrivateClass1 ^ four errors found
In the same package, an attempt to declare a public or protected subclass will fail. Only the private and private [this] subclasses are allowed. Also, PrivateClass2 is in scopeA2, so you can't declare it outside of scopeA2. A simple attempt to declare a class that uses PrivateClass1 in an unrelated scopeB also fails.
Therefore, when applied to a type, private [this] and Java have the same package private visibility.
Next, let's check the visibility at the type level, private [T], T is a type.
/ / code-examples/BasicOOP/scoping/private-type-wont-compile.scala / / WON'T COMPILE package scopeA {class PrivateClass1 (private [PrivateClass1] val privateField1: Int) {private [PrivateClass1] val privateField2 = 1 def equalFields (other: PrivateClass1) = (privateField1 = = other.privateField1) & & (privateField2 = = other.privateField2) & & (nested = = other.nested) class Nested {private [Nested] val NestedField = 1} private [PrivateClass1] val nested = new Nested val nestednestedNested = nested.nestedField / / ERROR} class PrivateClass2 extends PrivateClass1 (1) {val field1 = privateField1 / / ERROR val field2 = privateField2 / / ERROR val nField = new Nested (). NestedField / / ERROR} class PrivateClass3 {val privateClass1 = new PrivateClass1 (1) val privateField1 = privateClass1.privateField1 / / ERROR val privateField2 = privateClass1.privateField2 / / ERROR val privateNField = privateClass1.nested.nestedField / / ERROR}}
Compiling this file produces the following output.
12: error: value nestedField cannot be accessed in PrivateClass1.this.Nested val nestednestedNested = nested.nestedField ^ 15: error: not found: value privateField1 val field1 = privateField1 ^ 16: error: not found: value privateField2 val field2 = privateField2 ^ 17: error: value nestedField cannot be accessed in PrivateClass2.this.Nested val nField = new Nested () .nestedField ^ 21: error: value privateField1 cannot be accessed in scopeA.PrivateClass1 val privateField1 = privateClass1.privateField1 ^ 22: error: value privateField2 cannot be accessed in scopeA.PrivateClass1 val privateField2 = privateClass1.privateField2 ^ 23: error: value Nested cannot be accessed in scopeA.PrivateClass1 val privateNField = privateClass1.nested.nestedField ^ 7 errors found
A member of private [PrivateClass1] is also visible to other instances, so the equalFields method can be compiled. Therefore, private [T] is not as strict as private [this]. Note that PrivateClass1 cannot access Nested.nestedField because that field is declared as private [Nested].
Prompt
When a member of T is declared as private [T], its behavior is equivalent to private. But it is different from private [this], which is more rigorous.
What happens if we change the scope of Nested.nestedField to privatize [PrivateClass1]? Let's take a look at how private [T] affects nested types.
/ / code-examples/BasicOOP/scoping/private-type-nested-wont-compile.scala / / WON'T COMPILE package scopeA {class PrivateClass1 {class Nested {private [PrivateClass1] val nestedField = 1} private [PrivateClass1] val nested = new Nested val nestednestedNested = nested.nestedField} class PrivateClass2 extends PrivateClass1 {val nField = new Nested (). NestedField / / ERROR} class PrivateClass3 {val privateClass1 = new PrivateClass1 Val privateNField = privateClass1.nested.nestedField / / ERROR}}
Compiling this file will result in the following output.
10: error: value nestedField cannot be accessed in PrivateClass2.this.Nested def nField = new Nested () .nestedField ^ 14: error: value nested cannot be accessed in scopeA.PrivateClass1 val privateNField = privateClass1.nested.nestedField ^ two errors found
NestedField is now visible to PrivateClass1, but it is still not visible outside PrivateClass1. This is what private does in Java.
Let's check its scope with a package name.
/ / code-examples/BasicOOP/scoping/private-pkg-type-wont-compile.scala / / WON'T COMPILE package scopeA {private [scopeA] class PrivateClass1 package scopeA2 {private [scopeA2] class PrivateClass2 private [scopeA] class PrivateClass3} class PrivateClass4 extends PrivateClass1 protected class PrivateClass5 extends PrivateClass1 private class PrivateClass6 extends PrivateClass1 private [this] class PrivateClass7 extends PrivateClass1 private [this] class PrivateClass8 extends scopeA2.PrivateClass2 / / ERROR private [this] class PrivateClass9 extends scopeA2.PrivateClass3} package ScopeB {class PrivateClass1B extends scopeA.PrivateClass1 / / ERROR}
Compiling this file produces the following output.
Error: class PrivateClass2 cannot be accessed in package scopeA.scopeA2 private [this] class PrivateClass8 extends scopeA2.PrivateClass2 ^ 19: error: class PrivateClass1 cannot be accessed in package scopeA class PrivateClass1B extends scopeA.PrivateClass1 ^ two errors found
Note that PrivateClass2 cannot be inherited outside of scopeA2, but PrivateClass3 can be inherited in scopeA because it is declared as private [ScopeA].
Finally, let's take a look at the type member scope effect at the package level.
/ / code-examples/BasicOOP/scoping/private-pkg-wont-compile.scala / / WON'T COMPILE package scopeA {class PrivateClass1 {private [scopeA] val privateField = 1 class Nested {private [scopeA] val nestedField = 1} private [scopeA] val nested = new Nested} class PrivateClass2 extends PrivateClass1 {val field = privateField val nField = new Nested (). NestedField} class PrivateClass3 {val privateClass1 = new PrivateClass1 val privateField = privateClass1.privateField val privateNField = privateClass1.nested.nestedField} package scopeA2 {class PrivateClass4 {private [scopeA2] val field1 = 1 private [scopeA] val field2 = 2}} class PrivateClass5 {val privateClass4 = new scopeA2.PrivateClass4 val field1 = privateClass4.field1 / / ERROR val field2 = privateClass4.field2} package scopeB {class PrivateClass1B extends scopeA.PrivateClass1 { Val field1 = privateField / / ERROR val privateClass1 = new scopeA.PrivateClass1 val field2 = privateClass1.privateField / / ERROR}}
Compiling this file will result in the following output.
28: error: value field1 cannot be accessed in scopeA.scopeA2.PrivateClass4 val field1 = privateClass4.field1 ^ 35: error: not found: value privateField val field1 = privateField ^ 37: error: value privateField cannot be accessed in scopeA.PrivateClass1 val field2 = privateClass1.privateField ^ three errors found
The only mistake is to try to access members of scopeA from an unrelated package scopeB, or to try to access members that are members of a nested package scopeA2.
Prompt
When a type or member is declared as private [P], P is a package that contains them, then this is equivalent to the package private visibility of Java.
Summary of visibility
Scala visibility statements are very flexible and have a consistent code of conduct. They provide detailed visibility control for all possible scopes, from the instance level (privateThis) to the package level (private [P]). For example, they make it easier to create components that expose types outside the top-level package, and well hide the implementation of types and type members within the component package.
Finally, we observed a "problem" of potential trait hidden members.
Prompt
Care must be taken when choosing the names of trait members. If two trait have members with the same name and the trait is used by the same instance, naming conflicts will occur even if both members are private.
Fortunately, the compiler will catch on to this problem.
Thank you for reading, the above is the content of "what is the method of Scala object-oriented programming". After the study of this article, I believe you have a deeper understanding of what the method of Scala object-oriented programming is, and the specific use needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.