Scala 面向对象编程
来源:互联网 发布:html怎么跟php文件连接 编辑:程序博客网 时间:2024/06/06 02:08
一 定义一个简单的类
1.1定义类,包含field以及方法
classHelloWorld {
private var name = "leo"
def sayHello() { print("Hello, " +name) }
def getName = name
}
1.2创建类的对象,并调用其方法
valhelloWorld = new HelloWorld
helloWorld.sayHello()
print(helloWorld.getName)
1.3定义函数括号问题
定义函数我们如果没有参数,可以不加括号,也可以加括号
但是定义的时候,没加,在调用的时候,就不要加,否则会报错;如果在定义的时候加那我们在调用的时候也加上
classHello{
var name = "nicky"
def hello(){print("Hello,"+name)}
def say = print(name+", welcome toyou!")
def getName = name
}
valhello = new Hello
scala>hello.hello()
Hello,nicky
scala>hello.say
nicky,welcome to you!
scala> hello.say()
<console>:13: error: Unit does not take parameters
hello.say()
这里就是定义函数没加括号,但是调用的时候加括号了,然后就报错
二 字段的get和set方法
2.1定义不带private var field的字段
varname = "icon"
此时,JVM会自动定义为private域,并且提供public的getter和setter方法
classHello {
var name = "nicky";
def hello(password:String){
println("Welcome you to ScalaWorld,"+name+", you password is "+password);
}
def say {
println("Are u "+name+"?")
println("yes!!")
}
}
objectTest {
def main(args:Array[String]):Unit = {
val hello = new Hello
hello.hello("123456")
hello.name = "Claria"
hello.say
}
}
运行结果:
Welcomeyou to Scala World,nicky, you password is 123456
Areu Claria ?
yes!!
2.2显示设置private 修饰变量
如果我们显式将字段定义成private,那么此时默认生成的getter和setter方法也是private的,对于其他类不可见。
classHello {
private var name = "nicky";
def say {
println("Are u "+name+"?")
println("yes!!")
}
}
objectTest {
def main(args:Array[String]):Unit = {
val hello = new Hello
hello.name ="Claria"//编译通不过
hello.say
}
}
2.3字段定义成valfield
如果我们定义字段valfield,那么只会生成get方法,而不会生成set方法,因为 val类型的变量,不能修改其值或者引用
class Hello {
val name ="nicky";
def say {
println("Are u "+name+" ?")
println("yes!!")
}
}
objectTest {
def main(args:Array[String]):Unit = {
val hello =new Hello
print(hello.name)//但是可以get
hello.name = "belly"//编译报错,不能set
hello.say
}
}
2.4不生成setter和 getter方法
如果希望既不生成setter函数也不希望生成getter函数
那么可以将字段声明为private[this]
class Hello {
private[this]var name = "nicky";
def say {
println("Are u "+name+" ?")
println("yes!!")
}
}
2.5private var field 和private[this]var field 比较
都只是能在本类使用
privatevar field 可以生成getter和setter方法,但是是私有的
private[this]var field 压根儿就没有get和set方法
言外之意,在同一类,privatevar field 可以set,但是private[this]var field 没有set,不能修改字段
2.6自定义get和set方法
如果只是希望简单的get和set方法,按照scala提供的语法规则就可以了,根据需要为field选择合适的修饰符,var、val、private、private[this]
但是,如果希望更好的控制get和set函数,你可以自定义getter和
Setter方法
class Student {
private varsname = "bell"
//自定义get方法
def name ="your name is " +sname
//自定义set方法
def name_=(newValue:String) {
sname = newValue
}
}
注意:
在自定义set方法=号左边不能有空格,否则编译报错
not found: value newValue
2.7如果不希望get和set方法暴露,但是希望其他类可以修改某一个字段的值
这时候我们可以将字段定义成private,然后提供一个方法修改该字段
class Student {
private varsname = "bell"
//自定义get方法
def name ="your name is " +sname
def updateName(sname:String):Unit = {
this.sname =sname;
}
}
2.8如果不希望当前private 字段被其他类的方法调用时访问,可以使用private[this]
比如,当前类Student,定义一个private字段 sname,如果希望两个实例的name进行比较,private可以实现;但是如果希望不同实例间不能访问该字段,则需要使用private[this]
class Student {
private varsname = "bell"
def name ="name is "+sname
def name_=(newVal:String){
sname = newVal
}
def access(s:Student):Unit = {
sname.equals(s.sname)
}
}
class Student {
private[this]var sname = "bell"
def name ="name is "+sname
def name_=(newVal:String){
sname = newVal
}
def access(s:Student):Unit = {
//value sname is not a member of com.scala.Student
sname.equals(s.sname)
}
}
2.9让Scala自动生成java风格的getter和setter方法
给field添加@BeanProperty注解即可
import scala.beans.BeanProperty
class Student {
@BeanProperty
var name:String = _
}
objectTest {
def main(args:Array[String]):Unit = {
val s =new Student
s.setName("nicky")
print(s.getName)
}
}
三 constructor 详解
3.1辅助构造器
在scala我们可以定义多个辅助构造函数,类似于Java构造方法重载
辅助构造方法之间可以相互调用,而且第一行必须调用主构造器
class Student {
private varname = "";
private varage = 0;
def this(name:String){
this();//主构造器
this.name =name;
}
def this(name:String,age:Int){
this(name);
this.age =age;
}
}
3.2主构造器
Scala中,主constructor是与类名放在一起的,与java不同。而且类中,没有定义在任何方法或者是代码块之中的代码,就是主constructor的代码,这点感觉没有java那么清晰
3.2.1普通构造参数
class Student(varname:String,var age:Int) {
println("you name is "+name+", age is "+age);
}
我们在初始化的时候,必须提供构造参数,否则报错
objectTest {
def main(args:Array[String]):Unit = {
//not enough arguments for constructor Student: (name: String, age: Int)
//com.scala.Student. Unspecified value parameters name, age.
val s =new Student
//提供构造参数就没有问题
val s1 =new Student("nicky",20)
}
}
3.2.2为构造参数提供默认值
class Student(varname:String ="郭靖",var age:Int = 25) {
println("you name is "+name+", age is "+age);
}
objectTest {
def main(args:Array[String]):Unit = {
//提供默认的构造参数,就不会报错
val s =new Student
//提供构造参数就没有问题
val s1 =new Student("nicky",20)
}
}
3.2.3构造参数没有生命变量类型和修饰符
如果构造参数没有生命变量类型和修饰符,诸如name:String,如果这样,如果在类里面有方法使用到了,那么该字段就是private[this]修饰;
如果没有方法用到该字段,则表示没有该字段,只是在初始化阶段使用
class Student(name:String,age:Int) {
println("you name is "+name+", age is "+age);
}
四 内部类介绍
Scala也有内部类,但是和Java优点不一样,如果不同的外部类实例的内部类实例是不一样的。
Java实现
import java.util.ArrayList;
import java.util.List;
publicclass Classes {
class Student {
private String name;
public Student(String name) {
super();
this.name =name;
}
}
List<Student>students = new ArrayList<Student>();
public Student getStudent(String name){
return new Student(name);
}
public staticvoid main(String[] args) {
Classesc1 = new Classes();
Students1 = c1.getStudent("Nicky");
c1.students.add(s1);
Classesc2 = new Classes();
Students2 = c2.getStudent("Bell");
c1.students.add(s2);
}
}
Scala实现
import scala.collection.mutable.ArrayBuffer
class Class {
class Student(valname: String) {}
val students =new ArrayBuffer[Student]
def getStudent(name:String) = {
new Student(name)
}
//产生外部类实例班级一
val c1 =new Class
//产生一个班级一的学生nicky
val s1 =c1.getStudent("nicky")
//把该学生放到班级一的学生集合里面
c1.students += s1
//============================================
//产生外部类实例班级二
val c2 =new Class
//产生一个班级二的学生nicky
val s2 =c2.getStudent("bell")
//把该学生放到班级一的学生集合里面,这时候会报错
//因为班级二的学生只能放到班级二的学生集合里
c1.students += s2
}
在Java里只要内部类是同一个类型,都可以加入到某一个班级中,而不用区别Student是哪一个Classes的实例
Scala里,即使内部类是同一个类型,也要区别Student属于的Class实例不一样
内部类访问外部类
方式一:外部类名.this.成员
class Classes(val name:String ="全真教") {
var age:Int = 0;
def this(age:Int){
this();
this.age = age;
}
class Student(val name: String) {
def info():Unit = {
Classes.this.print();
println("Outer Class Name: "+Classes.this.name+"; Inner Class Name: "+name)
}
}
def print(){
println(this.name+" "+this.age)
}
}
方式二 :别名,outrer=>
但是必须放在第一行
class Classes(val name:String ="全真教") {
outer =>
class Student(val name: String) {
def info():Unit = {
outer.print();
println("Outer Class Name: "+outer.name+"; Inner Class Name: "+name)
}
}
var age:Int = 0;
def this(age:Int){
this();
this.age = age;
}
def print(){
println(this.name+" "+this.age)
}
}
五 面向对象编程之object
5.1object特性
普通class没有提供一些静态成员变量,不能定义所有实例对象共享的东西;然而这些东西,Scala也可以定义,定义在object里的;
第一次调用object的方法时,就会执行object的构造器,而且这个构造器只能在object第一次被调用时执行一次,以后再次调用不会执行了
object通常用于作为单例模式的实现,或者放class的静态成员,比如一些工具方法
objectPerson{
private var eyeNum:Int = 2
print("this is Person object,constructor is exexuting")
def getEyeNum = eyeNum
}
scala>Person.getEyeNum
thisis Person object, constructor is exexutingres4: Int = 2
scala>Person.getEyeNum
res5:Int = 2
5.2伴生对象和伴生类
如果有一个class,还有一个跟class同名的object,那么简称这个object是class的伴生对象;class是object的伴生类
伴生类和伴生对象必须同在一个scala文件
伴生类和伴生对象可以互相访问private字段
/**
*作为Person object的伴生类
*/
class Person {
var name:String =""
var age:Int =0
private varaddress:String =""
private[this]var hobby:String = ""
def this(name:String,age:Int,address:String,hobby:String){
this()
this.name =name
this.age =age
this.address =address
this.hobby =hobby
}
def output =println("Name -->"+name+" Age -->"+age+" address -->"
+address+" hobby -->"+hobby+" EyeNum -->"+Person.eyeNum)
}
/**
*作为class person的伴生对象
*/
objectPerson{
private vareyeNum:Int = 2;
println("这是Person object,构造器开始执行")
def getEyeNum =eyeNum
}
5.3object继承
object也可以继承抽象类,并覆盖抽象类中的方法
abstractclass Hello(varmessage:String) {
def sayHello(name:String):Unit{}
}
objectHelloImpl extends Hello("Welcome you"){
override defsayHello(name:String):Unit = {
println(message+", "+name);
}
}
5.4apply方法
>>apply方法是一个object非常特殊的方法,通常在伴生对象object中实现该方法,并在其中实现构造伴生类的的对象的功能.
>>创建伴生类对象时,通常不会使用new 类名方式,而使用类名()的方式,隐式调用伴生对象的apply方法这样会让创建对象更加简洁.
Array类的伴生对象就实现了接收可变数量参数,并创建一个Array对象的功能
def apply[T: ClassTag](xs: T*): Array[T] = {
//根据传递进来的可变数量参数长度初始化数组
val array = new Array[T](xs.length)
var i = 0
//遍历数组,并把传递进来的值赋给每一个元素
for (x <- xs.iterator) { array(i) = x; i +=1 }
//返回数组对象
array
}
定义自己的伴生类和伴生对象
class Human(valname:String,val age:Int){
println("初始化Human class")
def info(){
println(this.name+" "+this.age)
}
}
objectHuman {
def say():Unit = {
println("Human can speak");
}
def apply(name:String,age:Int):Human = {
new Human(name,age)
}
def main(args:Array[String]):Unit = {
val human =Human("郭靖",25)
Human.say()
human.info()
}
}
5.5main方法
我们需要一个执行代码的入口类,在Scala里面,我们也需要定义一个main方法,而且这个main方法只能在object里面
defmain(args:Array[String]):Unit = {
val human =Human("郭靖",25)
Human.say()
human.info()
}
除了main方法以外,我们还可以集成App Trait,然后将需要在main方法执行的代码直接作为object的构造器代码,而且用args可以接收传入的参数
object HelloWorldextends App {
if (args.length > 0) {
print("arguments list: ")
for(i <- 0 until args.length ) {
print(args(i) +" ")
}
}else{
print("No any arguments passed!")
}
}
AppTrait的工作原理:
App继承自DelayedInitTrait,scalac 命令进行编译时,会把AppTrait的object的构造器代码放到DelayedInitTrait的delayedInit方法执行
5.6object实现枚举功能
Scala没有直接提供类似于Java的枚举功能,如果Scala要实现枚举,需要extendsEnumeration类,并且调用value方法来初始化枚举值
objectSeason extendsEnumeration {
val SPRING,SUMMER, AUTUMN,WINTER = Value
}
还可以通过Value传入枚举值的id和name,通过id和toString可以获取;还可以通过id和name来查找枚举值
objectSeason extends Enumeration {
val SPRING =Value(0,"spring")
val SUMMER =Value(1,"summer")
val AUTUMN =Value(2,"autumn")
val WINTER =Value(3,"winter")
def main(args:Array[String]){
for (i <-Season values) {
println(i)
}
}
}
5.7 object 与 class区别
>>关键字不一样
>>object不能接受带参数的构造器;class 可以接收带参数的构造器
>>object可以定义一些类似静态的成员变量,然后所有实例共享;class不能定义静态成员变量
>>object可以定义apply方法实例化class
>>class可以直接访问
六 面向对象之继承
6.1extends
Scala的继承和Java基本差不多,都是用extends关键字;
父类如果是final,则不能被继承;
父类方法或者字段是final,则不能被子类覆盖
6.2override & super
覆盖父类方法或者var|val字段。使用关键字override
调用父类的方法或者字段等使用关键字super
而且子类的valfield还可以覆盖父类的valfield的getter方法;只要在子类中使用override关键字即可
class Person {
private var name = "leo"
def getName = name
}
class Student extends Person {
private var score = "A"
def getScore = score
override def getName = "Hi, I'm " + super.getName
}
6.3isInstanceOf 和 asInstanceOf
isInstanceOf[类名]:判断是否是某一个类的实例;
asInstanceOf[类名]:将某一个类的实例强制转换
Java实现
publicclass Person {
Stringname = null;
public Person(String name) {
this.name =name;
}
public String getName() {
return name;
}
public void setName(Stringname) {
this.name =name;
}
}
publicclass Student extends Person{
private doublescore;
public Student(String name,double score) {
super(name);
this.score =score;
}
public void outPut() {
System.err.println(this.name+" "+this.score);
}
public double getScore() {
return score;
}
public void setScore(doublescore) {
this.score =score;
}
}
public staticvoid main(String[] args) {
Personp = new Student("洪七公", 95.5);
Students = null;
if (pinstanceof Student) {
s = (Student) p;
s.outPut();
}
}
Scala实现:
class Person(varname:String,var age:Int) {
println("Person 主构造器开始工作!")
val skin:String =null;
def say:Unit =print("说人话");
}
class Student(name:String,age:Int,var score:Double) extends Person(name,age){
println("Student 主构造器开始工作!")
var classes:String =null;
def this(name:String,age:Int,score:Double,classes:String){
this(name,age,score);
this.classes =classes;
println("Student 辅助构造器开始工作!");
}
override valskin:String ="黄色"
override defsay:Unit = println("讲汉语");
}
objectTest {
def main(args: Array[String]): Unit = {
val p:Person =new Student("西门吹雪",25,85,"剑客班");
var s:Student =null;
if (p.isInstanceOf[Student]) {
s = p.asInstanceOf[Student];
}
}
}
注意:
val p:Person = new Student("西门吹雪",25,85,"剑客班");
p.isInstanceOf[Person] = tue ?
其实过程是:
它首先判断是不是Person的实例,不是,然后继续判断是不是Person子类的实例,如果还不是返回false,如果是返回true.
==>所以这里的判断类型不算是很精确的
6.4getClass 和 classOf
我们知道:isInstanceOf只能判断出对象是否是指定类以及其子类的对象,而不能精确判断出,对象就是指定类的对象。
如果要求精确地判断对象就是指定类的对象,那么就只能使用getClass和classOf了
对象.getClass可以精确获取对象的类,classOf[类]可以精确获取类,然后使用==操作符即可判断
scala>class Person
definedclass Person
scala>class Student extends Person
definedclass Student
scala>val p:Person = new Student
p:Person = Student@48d5f34e
scala>p.isInstanceOf[Person]
res3:Boolean = true
scala>p.isInstanceOf[Student]
res4:Boolean = true
scala>p.getClass == classOf[Person]
res5:Boolean = false
scala>p.getClass == classOf[Student]
res6:Boolean = true
6.5使用模式匹配进行类型判断
使用模式匹配,功能性上来说,与isInstanceOf一样,也是判断主要是该类以及该类的子类的对象即可,不是精准判断的
scala>p match {
| case per:Person => println("It'sa Person Object")
| case _ => println("UnknownType!!")
| }
It'sa Person Object
6.6protected
Scala也可以使用protected关键字来修饰字段和方法,protected修饰的字段和方法在子类是不需要通过super去调用的,直接就可以访问
protected[this]:只能在当前子类对象中访问父类的字段和方法,无法通过其他子类对象访问父类的字段和方法
class Human {
protected varname: String ="nicky"
protected[this]var hobby: String = "game"
}
class Workerextends Human {
def sayHello =println("Hello, "+ name)
def makeFriends(s: Student) {
//s.hobby 就会编译报错,因为不是当前子类,而是父类的其他子类
println("my hobby is " + hobby + ", your hobby is " + s.hobby)
}
}
6.7调用父类的构造器
Scala中,每个类可以有一个主constructor和任意多个辅助constructor,而每个辅助constructor的第一行都必须是调用其他辅助constructor或者是主constructor;因此子类的辅助constructor是一定不可能直接调用父类的constructor的
只能在子类的主constructor中调用父类的constructor,以下这种语法,就是通过子类的主构造函数来调用父类的构造函数
注意!如果是父类中接收的参数,比如name和age,子类中接收时,就不要用任何val或var来修饰了,否则会认为是子类要覆盖父类的field,举个例子:
class Human(varname:String,var age:Int) {
def outPut:Unit = {
println(this.name+ "<<>>" + this.age)
}
}
/*
* var name:String, var age:Int编译报错
*认为是子类要覆盖父类的name和age属性
*/
class Worker(varname:String,var age:Int)extends Human(name,age) {
}
如果我把变量名改一下:
Woker(varname1:String,var age1:Int) extends Human(name1,age1) {
}
这样就不会报错,的确,确实可以如此,但是,我们却增加了2个字段,浪费空间
标准写法:
class Worker(name:String,age:Int,var score:Double) extends Human(name,age) {
def this(name:String) {
this(name,0, 0)
}
def this(name:String,age: Int) {
this(name,age, 0)
}
}
另外,自定义辅助的构造函数参数类型不能和主构造函数相同,否则认为重复了,会编译报错
6.8匿名内部类
可以定义一个类的没有名称的子类,并直接创建其对象,然后将对象的引用赋予一个变量。这个就是匿名内部类,我们甚至可以将该匿名内部类对象传递给其他函数
匿名内部类在java实现:
new父类构造器(参数列表)|实现接口() {
//匿名内部类的类体部分
}
publicabstract class Bird {
private String name;
public String getName() {
return name;
}
public void setName(Stringname) {
this.name =name;
}
public abstractint fly();
}
publicinterface Animal {
public abstractvoid feed();
public abstractvoid getType();
}
new Bird() {
@Override
public int fly() {
return 1000;
}
};
Animalanimal = new Animal() {
@Override
public void getType() {
System.out.println("飞禽");
}
@Override
public void feed() {
System.out.println("觅食");
}
};
}
Scala实现
class Human(varname:String,var age:Int) {
def outPut:Unit = {
println(this.name+ "<<>>" + this.age)
}
}
objectTest {
def main(args: Array[String]): Unit = {
val h:Human =new Human("杨过",20){
override defoutPut:Unit = {
println(this.name+ "#####" + this.age)
}
}
h.outPut
}
}
6.9抽象类和抽象方法
在Scala抽象的定义和java一样:
>>如果在父类中,有某些方法无法立即实现,而需要依赖不同的子来来覆盖,重写实现自己不同的方法实现。此时可以将父类中的这些方法不给出具体的实现,只有方法签名,这种方法就是抽象方法。
>>而一个类中如果有一个抽象方法,那么类就必须用abstract来声明为抽象类,此时抽象类是不可以实例化的
>>但是,在子类中覆盖抽象类的抽象方法时,不需要使用override关键字
>>如果父类子段没有初始化值,那么父类必须是抽象的,而且子类需要重写这个属性,但是不用使用关键字override,否则子类编译会报错
abstractclass Animal(name:String) {
var age:Int
def eat:Unit
def sleep(){
println("sleep 8 hours")
}
}
class Lion(name:String)extends Animal(name){
var age:Int = 0
def eat:Unit = {
println("狮子吃肉");
}
}
objectTest {
def main(args: Array[String]): Unit = {
val lion =new Lion("美洲狮");
lion.eat
lion.sleep()
}
}
七 面向对象编程之Trait
7.1Trait基础知识
7.1.1trait作为接口使用
我们可以将trait作为接口使用,这时候和Java的接口非常类似
特性:
>>trait中可以定义抽象方法,和抽象类定义抽象方法一样,不给出具体实现即可
>>类可以使用extends集成trait,这里和Java有所区别,Scala没有实现的概念,无论是继承类还是trait都用extends
>>类继承之后,必须实现其中的抽象方法,实现不需要override
>>Scala不允许类多继承,但是trait可以多继承,使用with关键字即可
traitHelloTrait {
def sayHello(name:String)
}
traitMakeFriendsTrait {
def makeFriends(p: Person)
}
import com.scala.traits.HelloTrait
import com.scala.traits.MakeFriendsTrait
class Person(valname:String)extends HelloTrait with MakeFriendsTrait with Serializable {
def sayHello(name:String):Unit = {
println("Hello, "+name)
}
def makeFriends(p: Person){
println("你好,我是"+name+",您的名字是"+p.name+"吗?")
}
}
objectTest {
def main(args: Array[String]): Unit = {
val p1:Person =new Person("张无忌")
p1.sayHello("周芷若")
val p2:Person =new Person("赵敏")
p1.makeFriends(p2)
}
}
Hello, 周芷若
你好,我是张无忌, 您的名字是赵敏吗?
7.1.2trait中定义具体方法
trait中不仅只是可以定义抽象方法,还可以定义具体方法,就像一个包含通用工具方法的东西;相当于时trait混入了类
traitLogger {
def log(message:String) = println(message)
}
class Person(valname:String)extends HelloTrait with MakeFriendsTrait with Logger with Serializable {
def sayHello(name:String):Unit = {
println("Hello, "+name)
}
def makeFriends(p: Person){
println("你好,我是"+name+",您的名字是"+p.name+"吗?")
log(name+"正在和"+p.name+"交朋友")
}
}
7.1.3trait中定义具体字段
Scala中的trait可以定义具体field,此时继承trait的类就自动获得了trait中定义的field;
但是这种获取field的方式与继承class是不同的:如果是继承class获取的field,实际是定义在父类中的;而继承trait获取的field,就直接被添加到了类中
traitLogger {
val logLevel:String ="info";
def log(message:String) = println(message)
}
class Person(valname:String)extends HelloTrait with MakeFriendsTrait with Logger with Serializable {
def sayHello(name:String):Unit = {
println("Hello, "+name)
}
def makeFriends(p: Person){
println("你好,我是"+name+",您的名字是"+p.name+"吗?")
log("日志级别: "+p.logLevel)
log(name+"正在和"+p.name+"交朋友")
}
}
7.1.4trait中定义抽象字段
trait可以定义抽象方法,继承trait的类,则必须覆盖抽象字段;但是继承trait的trait,可以不必实现;可以理解为抽象类继承抽象类,然后只有继承抽象类的类才会实现具体的东西
trait SayHello {
val msg: String
def sayHello(name: String) = println(msg + ", " + name)
}
class Person(val name: String) extends SayHello {
val msg: String = "hello"
def makeFriends(p: Person) {
sayHello(p.name)
println("I'm " + name + ", I want to make friends with you!")
}
}
7.2Trait高级知识
7.2.1为实例对象混入trait
traitLogged {
def log(msg:String) {}
}
traitMyLogger extendsLogged {
override deflog(msg: String) { println("log: "+ msg) }
}
class Person(valname: String)extends Logged {
def sayHello {
println("Hi, I'm " + name)
log("sayHello is invoked!")
}
}
objectTest {
def main(args: Array[String]): Unit = {
val p =new Person("金毛狮王")with MyLogger
p.sayHello
}
}
7.2.2trait调用链
>>首先,有一个parent trait,然后类要继承的所有trait都来继承parent trait
>>每一个trait实现父trait或者重写父trait 需要处于调用链上的方法,每一个方法调用完后,都调用一下super.方法名
>>类中调用多个trait中都有的这个方法时,首先会从最右边的trait的方法开始执行,然后依次往左执行,形成一个调用链条
>>这种特性非常强大,其实就相当于设计模式中的责任链模式的一种具体实现依赖
traitHandler {
def handle(data:String) {}
}
traitDataValidHandler extendsHandler {
override defhandle(data:String) {
println("数据校验: "+data);
super.handle(data)
}
}
traitSignatureValidHandler extendsHandler {
override defhandle(data:String) {
println("检查签名: "+ data)
super.handle(data)
}
}
7.2.3trait中覆盖抽象方法(有何意义)
>>在trait中,是可以覆盖父trait的抽象方法的
>>但是覆盖时,如果使用了super.方法的代码,则无法通过编译。因为super.方法就会去掉用父trait的抽象方法,此时子trait的该方法还是会被认为是抽象的
>>如果要通过编译,就得给子trait的方法加上abstractoverride修饰
trait Logger {
def log(msg: String)
}
trait MyLogger extends Logger {
abstract override def log(msg: String) { super.log(msg) }
}
7.2.4混合使用trait的具体方法和抽象方法
>>在trait中,可以混合使用具体方法和抽象方法
>>可以让具体方法依赖于抽象方法,而抽象方法则放到继承trait的类中去实现
>>这种trait其实就是设计模式中的模板设计模式的体现
Java实现:
publicabstract class CaffeineBeverage {
/**
*
*@desc
* 模板方法,用来控制泡茶与冲咖啡的流程
* 申明为final,不希望子类覆盖这个方法,防止更改流程的执行顺序
*@return void
*/
final voidprepareRecipe(){
boilWater();
brew();
pourInCup();
addCondiments();
}
/**
*@desc
* 将brew()、addCondiment()声明为抽象类,具体操作由子类实现
*@return void
*/
abstract void brew();
abstract void addCondiments();
void boilWater(){
System.out.println("Boiling water...");
}
void pourInCup(){
System.out.println("Pouring into Cup...");
}
}
publicclass Coffee extends CaffeineBeverage{
void addCondiments() {
System.out.println("Adding Sugar and Milk...");
}
void brew() {
System.out.println("Dripping Coffee through filter...");
}
}
Scala实现:
traitCook {
def brew
def addCondiments
def boilWater(){
println("Boiling water...")
}
def pourInCup(){
println("Pouring into Cup...")
}
def prepareRecipe():Unit = {
boilWater()
brew
pourInCup()
addCondiments
}
}
import com.scala.traits.Cook
class Teaextends Cook{
def addCondiments(){
println("Adding Sugar and Milk...");
}
def brew(){
println("Dripping tea through filter...");
}
}
7.2.5trait的构造机制
trait也是有构造代码的概念,我们解读以下整个流程:
>>如果有父类,先构造父类
>>如果父类继承trait,先构造trait
>>如果trait有多个,从左右到右依顺序构造
>>如果trait还有父trait,那么父trait先构造
>>如果多个trait继承自同一个trait,这个trait只会被构造一次
traitHandler {
println("Handler Trait 构造函数执行...");
def handle(data:String) {}
}
traitDataValidHandler extendsHandler {
println("DataValidHandler Trait 构造函数执行...");
override defhandle(data:String) {
println("数据校验: "+data);
super.handle(data)
}
}
traitSignatureValidHandler extendsHandler {
println("SignatureValidHandler Trait 构造函数执行...");
override defhandle(data:String) {
println("检查签名: "+ data)
super.handle(data)
}
}
class Person(valname: String)extends SignatureValidHandler withDataValidHandler {
println("Person 类主构造函数执行...")
def sayHello = {println("Hello, "+ name); handle(name) }
}
class Student(name:String) extends Person(name){
println("Student 主构造函数执行...");
}
objectTest {
def main(args: Array[String]): Unit = {
val s:Student =new Student("林晚荣")
}
}
运行结果:
Handler Trait 构造函数执行...
SignatureValidHandler Trait 构造函数执行...
DataValidHandler Trait 构造函数执行...
Person 类主构造函数执行...
Student 主构造函数执行...
7.2.6trait字段的初始化
默认情况下,Scala中,trait是没有接收参数的构造函数的,这是trait与class的唯一区别,但是如果需求就是要trait能够对field进行初始化,该怎么办呢?只能使用Scala中非常特殊的一种高级特性——提前定义
下面代码会报空指针异常:因为trait先于子类构造,但是trait这会儿msg还没有初始化呢
traitSayHello {
val msg:String
println(msg.toString)
}
class Personextends SayHello{
val msg:String ="init";
}
objectTest {
def main(args: Array[String]): Unit = {
val p =new Person
}
}
方式一:
class Personextends {
val msg:String ="init";
} withSayHello
方式二:使用lazyvalue
traitSayHello {
lazy valmsg: String = null
println(msg.toString)
}
class Personextends SayHello{
override lazyval msg:String ="init";
}
objectTest {
def main(args: Array[String]): Unit = {
val p =new Person
}
}
方式三:
val p =new {
val msg: String = "init"
} with Personwith SayHello
7.2.7让trait继承类
在Scala中,trait也可以继承自class,此时这个class就会成为所有继承该trait的类的父类
class MyUtil {
def printMessage(msg:String) = println(msg)
}
traitLogger extends MyUtil {
def log(msg:String) = printMessage("log: " + msg)
}
class Person(valname: String)extends Logger {
def sayHello {
log("Hi, I'm " + name)
printMessage("Hi, I'm " + name)
}
}
- Scala 面向对象编程
- scala面向对象编程
- Scala面向对象编程
- Scala面向对象编程
- Scala面向对象编程之对象
- Scala编程指南:面向对象编程
- Scala-面向对象的函数编程
- Scala 的面向对象编程基础
- Scala 的面向对象编程实践(一)
- Scala 的面向对象编程实践(二)
- Scala 的面向对象编程实践(三)
- Scala学习之路----面向对象编程
- Scala基础—面向对象编程示例
- Scala学习—面向对象编程之类
- scala学习笔记:面向对象编程基础
- 第7节:scala面向对象编程
- Scala学习笔记(二)Scala面向对象编程
- Scala学习—面向对象编程之对象
- Android开发中遇到的问题收集(2)
- Mybatis获取当前sqlSession,Mybatis清空当前Session的缓存
- spring 的属性获取,取值.注解下的类和配置分离.
- css3中transform-style的用法详解
- 项目基本构造搭建以及xml配置文件
- Scala 面向对象编程
- Tomcat启动时报 java.lang.OutOfMemoryError: Java heap space
- MP4文件:Web播放器不能播放问题,不能边下边播问题的解决方案
- Swift : 在VoIP应用中接入iOS10中的CallKit框架--即接听电话使用系统原生界面
- 对/etc/rc.d/init.d目录的一点理解
- 百度API接口下,设置已建覆盖物(椭圆或多边形)的填充色为透明
- OKHttp使用完全解析
- 手把手带你实现Android增量更新
- 非常简单而又非常完整的R语言主成分分析实例