Detailed explanation of Scala's inheritance

1. Extend the class
Use the extends keyword to extend the class

class Employee extends Person {
	var age = 10
	...
}

In the definition, give the fields and methods required by the subclass but not the superclass, or override the methods of the superclass. Like Java, you can declare a class as final, so that it cannot be extended, and you can declare the method or field as final to ensure that they cannot be overridden.

2. Rewrite method
To override a non-abstract method in scala, you must use the override modifier, for example:

public class Person {
  ...
  override def toString = getClass.getName + "[name=" + name + "]"
}

The override modifier can give an error message in many common situations:
1. Misspell the method name to be rewritten
2. Accidentally use the wrong parameter type in the new method
3. Introduce a new one in the superclass Method, and this new method conflicts with the methods of subclasses

The method of calling the super class in scala is the same as java, using the super keyword:

public class  Employee extends Person {
	...
	override def toString = super.toString
}

super.toString will call the toString method of the super class, namely Person.toString.

3. Type checking and conversion To
test whether an object belongs to a given class, you can use the isInstanceOf method. If the test is successful, you can use the asInstanceOf method to turn the reference into a subclass reference.

if (p.isInstanceOf[Employee]) {
  val s = p.asInstanceOf[Employee]  //s类型为Employee
}

If p points to an object of Employee class and subclasses (for example, if there is a Manager), then p.isInstanceOf [Employee] will succeed and return true.
If p is null, p.isInstanceOf [Employee] will return false, and p.asInstanceOf [Employee] will return null.
If p is not an Employee, then p.asInstanceOf [Employee] will throw an exception.
If you want to test that p points to an Employee object but not its subclass, you can:

if (p.getClass == classOf[Employee])

The classOf method is defined in the scala.Predef object, so it will be automatically introduced.
Scala's type checking and conversion compared
Insert picture description here
with Java: But compared with type checking and conversion, pattern matching is usually a better choice, such as:

p match {
  case s: Employee => ... 
  case _ => ...
}

4. Protected fields and methods
Like Java, you can declare fields or methods as protected. Such members can be accessed by any subclass, but cannot be seen by other locations.
Unlike Java, protected members are not visible to the package to which the class belongs. If you need this visibility, you can use the package modifier.
There is also a protected [this], which sets the access permission to the current object, similar to private [this].

5. Construction of superclasses
We know that a class has a main constructor and any number of auxiliary constructors. Each main constructor must
start with a call to a previously defined auxiliary constructor or main constructor. The consequence of this is that auxiliary constructors can never directly call superclass constructors.
Auxiliary constructors of subclasses will eventually call the main constructor. Only the main constructor can call the superclass constructor.
The main constructor is intertwined with the class definition. The way to call the superclass constructor is also intertwined.
Such as:

class Employee(name: String,age: Int,val salary: Double) extends Person(name,age)

This code defines a subclass Employee and a main constructor Employee (name: String, age: Int, val salary: Double) that
calls the superclass constructor. Interweaving the class and the constructor can make the code more concise , It may be better to understand the parameters of the main constructor as the parameters of the class. For example, Employee class has three parameters
name, age, salary, two of which are passed to the super class. If it is Java, it will be much more complicated:

public class Employee extends Person {
  private double salary;
  public Employee(String name,Int age, double salary) {
	super(name,age);
	this.salary = salary;
  }
}

The Scala class can extend the Java class. In this case, its main constructor must call a construction method of the Java superclass, such as:

class Square(x: Int,y: Int,width: Int) extends java.awt.Rectangle(x,y,width.width)

6. Rewrite field The
Scala field consists of a private field and a valuer / value changer method. You can use another val field with the same name to rewrite a val (or def without parameters).
The subclass has a private field and a public getter method, and this getter method overrides the super class getter method. Such as:

class Person(val name: String) {
  override def toString = getClass.getName + name
}
class SecretAgent(codename: String) extends Person(codename) {
  override val name = "secret"
  override val toString = "secret"
}

The above example shows the rewriting mechanism, but it is more rigid, and it is more common to use val to rewrite abstract defs, such as:

abstract class Person {
  def id: Int   //每个人都有某一种方式计算id
  ...
}
class Student(override val id: Int) extends Person

Rewriting has the following restrictions:
1.def can only rewrite another def
2.val can only rewrite another val or def without parameters
3.var can only rewrite another abstract var

7. Anonymous subclasses Like
Java, an anonymous subclass can be created by including code blocks with definitions or rewrites, such as:

val alien = new Person("Fred") {
  def greeting = "Grettings, Earthing, my name is Fred"
}

Technically speaking, this creates an object of structure type, which is marked as Person {def greeting: String}, we can use this type as the definition of the parameter type:

def meet(p: Person{def greeting: String}) {
  println(p.name + "says" + p.greeting)
}

8. Abstract class
Like Java, you can use the abstract keyword to mark a class that cannot be instantiated. Usually this is because one or more of its methods are not completely defined, such as:

abstract class Person(val name: String) {
  def id: Int //没有方法体,这是一个抽象方法
}

Here we say that everyone has an id, but we do n’t know how to calculate it. Every specific Person subclass must give an id method. Unlike Scala, unlike Java, we do n’t need to use the abstract keyword , Just omit the method body, but like Java, if there is at least one abstract method in a class, the class must be declared as abstract.
When overriding the abstract method of a superclass in a subclass, there is no need to use the override keyword.

class Employee(name: String) extends Person(name) {
  def id = name.hashCode //无需override
}

9. Abstract field
In addition to abstract methods, a class can also have an abstract field, an abstract field is a field without an initial value, such as:

abstract class Person {
 val id: Int   //没有初始化,这是一个带有抽象的getter方法的抽象字段
 var name: String //另一个抽象字段,带有抽象getter和setter方法
}

This class defines abstract getter methods for the id and name fields, and abstract setter methods for the name field. The generated Java class has no fields.
Specific subclasses must provide specific fields, such as:

class Employee(val id: Int) extends Person { //子类有具体的id属性
  var name = ""  //和具体点的name属性
} 

As with methods, the override keyword is not required when rewriting the abstract field of a superclass in a subclass.

10. Construction order and definition
in advance When we rewrite val in the subclass and the constructor in the superclass also uses this value, it may cause some bad results.
For example, animals can perceive the surroundings, assuming that the default animal can see 10 units in front, then we can:

class Creature {
  val range: Int = 10
  val env: Array[Int] = new Array[Int](range)
}
//但蚂蚁是近视的
class Ant extends Creature {
  override val range = 2
}

Now we will face a problem, the range value is used in the super class constructor initialization, and the super class constructor runs before the sub class constructor. The specific process is as follows:
1. Ant's constructor calls Creature's constructor before doing its own construction.
2. The constructor of Creature sets its range field to 10.
3. In order to initialize the env array, Creator's constructor calls the range () evaluator.
4. This method is rewritten to output the value of the range field of the Ant class (not yet initialized).
5. The range method returns 0 (this is the initial value of all integer fields when the object is allocated space).
6. env is set to an array of length 0.
7. The Ant constructor continues to execute, setting its range field to 2.
Although the range field may seem to be 2, but env is set to an array of length 0, the lesson here is that the value of val should not be relied on in the constructor.
In Java, when you call a method in the superclass's constructor, you will encounter a similar problem. The called method may be rewritten by the subclass, so it may not behave as expected. In fact, this is the core of the problem. Where, the range expression calls the getter method.
There are the following solutions:
1. Declare val as final. Safe but not flexible.
2. Declare val as lazy in the superclass. Safe but not efficient.
3. Use the pre-defined syntax in the subclass.
Define the syntax in advance to initialize the val field of the subclass before the superclass constructor is executed:

class Ant extends {
  override val range = 2
} with Creature
Published 14 original articles · Like1 · Visits 684

Guess you like

Origin blog.csdn.net/qq_33891419/article/details/103853826