The main contents of this section
- Trait Construction Order
- Comparison of trait and classes
- Pre-defined and lazy loading
- Trait extension Classes
- Self type
1 Trait Construction Order
In the previous note, we mentioned that for trait that do not have a specific implementation and a field, it will eventually generate a bytecode file that is equivalent to the interface in Java, whereas for trait with specific implementations and fields, the bytecode file is compiled with the abstract class in Java, It has its own way of achieving the Scala language. Therefore, for trait it also has its own constructor, and the trait constructor consists of the initialization of the field and the statements in the other trait body, the following is its code demonstration:
Package Cn.scala.xtwyImportJava.io.PrintWritertrait logger{println ("Logger") def log(msg:string):Unit}trait FileLogger extends logger{println ("Filgelogger") Val fileoutput=new PrintWriter ("File.log") Fileoutput.println ("#") def log(msg:string):unit={fileoutput.print (msg) Fileoutput.flush ()}}object traitdemo{ def main(args:array[string]):Unit = {//anonymous class new filelogger{}.log ("Trat Demo"}}//printout is: loggerfilgelogger//create file File.log, content is#Trat Demo
It is not difficult to discover that when creating an anonymous class object, the constructor for the logger class is called first, and then the constructor of the FileLogger is called. The constructors are actually executed in the following order:
1. If there is a superclass, the constructor for the superclass is called First
2. If there is a parent trait, it calls the constructor of the parent trait,
2, according to the inheritance hierarchy. If there are more than one parent trait, the order is executed from left to right
3. This class is not constructed until all the parent and parent trait have been constructed
class Personclass Student extends Person with FileLogger with Cloneable上述构造器的执行顺序为:1 首先调用父类Person的构造器2 调用父trait Logger的构造器3 再调用trait FileLogger构造器,再然后调用Cloneable的构造器4 最后才调用Student的构造器
2 comparison of trait and classes
As you can see from the previous section, trait has its own constructor, which is a parameterless constructor and cannot define a constructor with parameters trait, namely:
//不能定义trait带参数的构造器trait FileLogger(msg:String)
In addition, there is no other difference between trait and the ordinary Scala class, which we mentioned in the previous lecture, that there can be specific, abstract fields in the trait, and that there can be concrete, abstract methods, even if there are no abstract methods in trait, such as:
//FileLogger里面没有抽象的方法trait FileLogger extends Logger{ println("FilgeLogger") val fileOutput=new PrintWriter("file.log") fileOutput.println("#") def log(msg:String):Unit={ fileOutput.print(msg) fileOutput.flush() }}
3. Pre-defined and lazy loading
The file name in the previous FileLogger is written dead as "File.log", the program does not have universality, this side to the front of the FileLogger transformation, the file name as a parameter form, the code is as follows:
import java.io.PrintWritertrait Logger{ def log(msg:String):Unit}trait FileLogger extends Logger{ //增加了抽象成员变量 val fileName:String //将抽象成员变量作为PrintWriter参数 val fileOutput=new PrintWriter(fileName:String) fileOutput.println("#") def log(msg:String):Unit={ fileOutput.print(msg) fileOutput.flush() }}
There is a problem with this design, although the subclass can rewrite the filename abstract member variable, the compilation can pass, but the actual execution will be null pointer exception, the full code is as follows:
PackageCn.scala.xtwyImportJava.io.PrintWriter trait Logger{ defLog (msg:string): Unit} trait filelogger extends Logger{ //Added abstract member variable ValFilename:string//Use abstract member variable as printwriter parameter Valfileoutput=NewPrintWriter (filename:string) fileoutput.println ("#")defLog (msg:string): unit={fileoutput.print (msg) Fileoutput.flush ()}} class person class Student extends person with FileLogger{ //student class overrides an abstract field in a FileLogger ValFilename="File.log"} Object traitdemo{ defMain (args:array[string]): Unit = {NewStudent (). log ("Trait Demo") }}
The code above is not problematic at compile time, but throws an exception when it is actually executed, with the following exceptions:
ExceptioninchThread"Main"Java. Lang. NullPointerExceptionAt Java. IO. FileOutputStream.<init> (Unknown Source) at Java. IO. FileOutputStream.<init> (Unknown Source) at Java. IO. PrintWriter.<init> (Unknown Source) at CN. Scala. Xtwy. FileLogger$class. $init $ (Traitdemo. Scala: A) at CN. Scala. Xtwy. Student.<init> (Traitdemo. Scala: A) at CN. Scala. Xtwy. Traitdemo$. Main(Traitdemo. Scala: -) at CN. Scala. Xtwy. Traitdemo. Main(Traitdemo. Scala)
The specific reason is the order of execution of the constructor,
class Student extends person with FileLogger{ //student class overrides an abstract field in a FileLogger ValFilename="File.log"}//In the new operation of the student class, it first calls the person constructor, which is no problem, and then calls the logger constructor, which is fine, but when it finally calls the FileLogger constructor, it executes the following two statements //Added abstract member variable ValFilename:string//Use abstract member variable as printwriter parameter Valfileoutput=NewPrintWriter (filename:string) when FileName is not assigned, is initialized toNULL, in the executionNewThrows a null pointer exception when PrintWriter (filename:string) operation
There are several ways to solve the previous problem:
1 pre-defined
Pre-defined is the initialization of a variable before a regular construct, with the complete code as follows:
PackageCn.scala.xtwyImportJava.io.PrintWriter trait Logger{ defLog (msg:string): Unit} trait filelogger extends Logger{ ValFilename:stringValfileoutput=NewPrintWriter (filename:string) fileoutput.println ("#")defLog (msg:string): unit={fileoutput.print (msg) Fileoutput.flush ()}} class person class Student extends person with FileLogger{ ValFilename="File.log"} Object traitdemo{ defMain (args:array[string]): Unit = {Vals=New{//Pre-defined Override ValFilename="File.log"} withStudent S.log ("predifined variable") }}
Obviously, the code written in this way is not elegant, and it is more difficult to understand. This can be done by lazy loading in the first lecture mentioned in the Lazy way
2 Lazy lazy load way
PackageCn.scala.xtwyImportJava.io.PrintWriter trait Logger{ defLog (msg:string): Unit} trait filelogger extends Logger{ ValFilename:string//Define the method as a lazy mode Lazy Valfileoutput=NewPrintWriter (filename:string)//The following statement does not appear, otherwise it will also error //So, it's a method inside the FileLogger constructor //The FileLogger will be executed when constructing the //fileoutput.println ("#") defLog (msg:string): unit={fileoutput.print (msg) Fileoutput.flush ()}} class person class Student extends person with FileLogger{ ValFilename="File.log"} Object traitdemo{ defMain (args:array[string]): Unit = {Vals=NewStudent S.log ("predifined variable") }}
The lazy method defines that Fileoutput is initialized only when it is actually used, and in the example, when S.log ("predifined variable") is called, Fileoutput is initialized, and filename is already assigned.
4 Trait Extension Classes
In the 2nd section of this section, we give the distinction between trait and classes, and we now understand that trait does not have any difference from ordinary classes except constructors with parameters, which means that trait can also extend other classes
trait Logger{ def log(msg:String):Unit}//trait扩展类Exceptiontrait ExceptionLogger extends Exception with Logger{ def log(msg:String):Unit={ println(getMessage()) }}
If you define a class that is mixed with exceptionlogger at this point, exception automatically becomes the superclass of this class, with the following code:
trait Logger{ defLog (msg:string): Unit} trait exceptionlogger extends Exception with Logger{ defLog (msg:string): unit={println (GetMessage ())}}//Class Unprintedexception extended from Exceptionlogger//Pay attention to the use of extends//At this time Exceptionlogger parent class exception automatically become//unprintedexception's parent class class unprintedexception extends exceptionlogger{ Override defLog (msg:string): unit={println ("") }}
Scala automatically eliminates conflicts when the unprintedexception extended class or the blended trait has the same parent class, for example:
//IOException具有父类Exception//ExceptionLogger也具有父类Exception//scala会使UnprintedException只有一个父类Exceptionclass UnprintedException extends IOException with ExceptionLogger{ override def log(msg:String):Unit={ println("")
5 Self type
The following code shows what a self type is.
class A{ //下面 self => 定义了this的别名,它是self type的一种特殊形式 //这里的self并不是关键字,可以是任何名称 self => val x=2 //可以用self.x作为this.x使用 self.x + this.x }
The following shows the usage scenarios in the inner class
class OuterClass { //定义了一个外部类别名 "here" class InnerClass { // 用outer表示外部类,相当于OuterClass.this println(outer.v1) }}
The following code defines the self type, which is not the purpose of the preceding alias,
trait X{}class B{ //self:X => 要求B在实例化时或定义B的子类时 //必须混入指定的X类型,这个X类型也可以指定为当前类型 self:X=>}
The existence of its own type is equivalent to making the current class "abstract", which assumes that the current object (this) also conforms to the specified type, because its own type this:x = = exists, and the current class constructs an instance that needs to satisfy the X type at the same time, giving the usage code of its own type:
trait X{ defFoo ()} class B{Self:x=>}//Class C Extension B must be mixed with trait X//Otherwise it will be an error. class C extends B with X{ defFoo () =println ("Self type demo")} Object selftypedemo extends App{printlnNewC (). Foo)}
Add a public number to find out more about the latest spark and Scala technical information
Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.
Getting started with Scala--11th quarter trait advanced