Scala 进阶
一 类与对象
scala是支持面向对象的,也有类和对象的概念。
创建类和对象
- 使用
class
关键字来定义类
- 使用
var
/val
来定义成员变量
- 使用
def
来定义成员方法
- 使用
new
来创建一个实例对象
var name:String = _,_表示使用默认值进行初始化
例如:String类型默认值是null,Int类型默认值是0,Boolean类型默认值是false…
val变量不能使用_来进行初始化,因为val是不可变的,所以必须手动指定一个默认值
main方法必须要放在一个scala的object
(单例对象)中才能执行
1 2 3 4 5 6 7 8 9 10 11 12 13
| object Demo4 { class Pe{ var name:String=_ var age:Int =_ def add(m:String) = print(m) private def a(a:String )= print(a) }
def main(args: Array[String]): Unit = { val pe = new Pe pe.add("mm") } }
|
geter和setter方法:
- scala会自动为成员变量生成scala语言的getter/setter
- scala的getter为
字段名()
,setter为字段名_=()
- 要生成Java的getter/setter,可以在成员变量上加一个
@BeanProperty
注解,这样将来去调用一些Java库的时候很有
1 2 3 4 5
| @BeanProperty var name:String = _
@BeanProperty val registerDate = new Date()
|
二构造器
1 主构造器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| object Demo5 { class Per(var name:String ="",var sex:String= ""){ print("构造") }
def main(args: Array[String]): Unit = { val ni = new Per("ni","男") println(ni.name) println(ni.sex)
val per = new Per() println(ni.sex) println(ni.name)
println(new Per(sex = "女").sex)
} }
|
2 辅助构造器
与定义方法一样,且方法名一定为this
注意:
辅助构造器第一行必须调用主构造器或者其他辅助构造器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| object Demo6 {
class Cus(var name: String = "", var add: String = "") { def this(fu: Array[String]) { this(fu(0), fu(1)) } def this(name:String){ this(name,"郑州") }
}
def main(args: Array[String]): Unit = { val cus = new Cus(Array[String]("aa","北京")) println(cus.add) val cus2 = new Cus("niu") println(cus2.add) print(cus2.name) }
}
|
- 主构造器直接在类名后面定义
- 主构造器中的参数列表会自动定义为私有的成员变量
- 一般在主构造器中直接使用val/var定义成员变量,这样看起来会更简洁
- 在辅助构造器中必须调用其他构造器(主构造器、其他辅助构造器)
三 单例对象 (类似于java中的static)
scala要比Java更加面向对象,所以,scala中是没有Java中的静态成员的。如果将来我们需要用到static变量、static方法,就要用到scala中的单例对象——object。可以把object理解为全是包含静态字段、静态方法的class,object本质上也是一个class。
定义object
定义单例对象和定义类很像,就是把class换成object
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 定义一个工具类,用来格式化日期时间 object DateUtils {
val simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm")
println("构造代码") def format(date:Date) = simpleDateFormat.format(date)
def main(args: Array[String]): Unit = { println { DateUtils.format(new Date()) }; } }
|
- 使用
object 单例对象名
定义一个单例对象,可以用object作为工具类或者存放常量
- 在单例对象中定义的变量,类似于Java中的static成员变量
- 在单例对象中定义的方法,类似于Java中的static方法
- object单例对象的构造代码可以直接写在花括号中
- 调用单例对象的方法,直接使用
单例对象名.方法名
,访问单例对象的成员变量也是使用单例对象名.变量名
- 单例对象只能有一个
无参的主构造器
,不能添加其他参数
1 2 3 4 5 6 7 8 9
| object Demo7 { object Dog{ val num=4; }
def main(args: Array[String]): Unit = { print(Dog.num) } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| object Demo8 { object Uf{ def add(): Unit ={ print("-" * 15) print("nicai") } }
def main(args: Array[String]): Unit = { Uf.add() } }
|
伴生对象
在Java中,经常会有一些类,同时有实例成员又有静态成员。如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class CustomerService {
private static Logger logger = Logger.getLogger("customerService");
public void save(String customer) { logger.info("添加一个用户"); }
public static void main(String[] args) { new CustomerService().save("客户"); } }
|
在scala中,可以使用伴生对象
来实现。
一个class和object具有同样的名字。这个object称为伴生对象
,这个class称为伴生类
注意:
半生类和伴生对象一样的名字
这两个要在同一个scala源文件中
这两个可以互相访问private属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| object Demo11 { class Ssm(){ def fin(): Unit ={ print(Ssm.name) } } object Ssm{ var name="nicai" }
def main(args: Array[String]): Unit = { val ssm = new Ssm ssm.fin() } }
|
private[this] 访问权限 表示只能在当前类中访问,伴生对象也不可访问
1 2 3 4 5 6 7 8 9 10 11 12
| object Demo12 { class De(private[this] var name:String,var age:Int)
object De{ def p(d:De): Unit =print(d.name) }
def main(args: Array[String]): Unit = { De.p(new De("aa",788)) } }
|
工具类案例:
需求:
编写一个工具类专门格式化日期时间
定义一个方法用于将日期转换为年月日字符串 2012-10-15
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| object Demo9 { object Forma{ private val format = new SimpleDateFormat("yyyy-MM-dd")
def fm(data:Date)=format.format(data) }
def main(args: Array[String]): Unit = { println(Forma.fm(new Date())) } }
|
apply方法
必须用在伴生对象中
我们之前使用过这种方式来创建一个Array对象。
这种写法非常简便,不需要再写一个new,然后敲一个空格,再写类名。如何直接使用类名来创建对象呢?
答案就是:实现伴生对象的apply方法
伴生对象的apply方法用来快速地创建一个伴生类的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| object Demo13 { class Person(var name:String, var age:Int) {
override def toString = s"Person($name, $age)" }
object Person { def apply(name:String, age:Int): Person = new Person(name, age)
def apply(name:String):Person = new Person(name, 20)
def apply(age:Int):Person = new Person("某某某", age)
def apply():Person = new Person("某某某", 20) }
def main(args: Array[String]): Unit = { println(Person("jjj").name) } }
|
- 当遇到
类名(参数1, 参数2...)
会自动调用apply方法,在apply方法中来创建对象
- 定义apply时,如果参数列表是空,也不能省略括号(),否则引用的是伴生对象
main方法
scala和Java一样,如果要运行一个程序,必须有一个main方法。而在Java中main方法是静态的,而在scala中没有静态方法。在scala中,这个main方法必须放在一个object中。
实例:
1 2 3 4 5
| object Main5 { def main(args:Array[String]) = { println("hello, scala") } }
|
也可以继承自App Trait(特质),然后将需要编写在main方法中的代码,写在object的构造方法体内。
1 2 3
| object Main extends App{ println("heoo") }
|
四继承
scala和Java一样,使用extends关键字来实现继承。可以在子类中定义父类中没有的字段和方法,或者重写父类的方法。
类和单例对象都可以从某个父类继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| object Demo14 { class Per(){ var name="" def getName()=this.name } class Student extends Per
def main(args: Array[String]): Unit = { val student = new Student student.name="ni" println(student.name) } }
|
单例继承
1 2 3 4 5 6 7 8 9 10 11 12 13
| object Demo15{ class Per(){ var name="" def a()=print("nicai") } object Stu extends Per
def main(args: Array[String]): Unit = { Stu.name="ni" println(Stu.name) } }
|
override 和supper
- 如果子类要覆盖父类中的一个非抽象方法,必须要使用override关键字
- 可以使用override关键字来重写一个val字段
- 可以使用super关键字来访问父类的成员
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| object Demo16 { class Per(){ val name:String ="22" var age:Int=0 def getName()=this.name } class Stu extends Per{ override val name: String = "nn"
override def getName(): String = "ni"+super.getName() }
def main(args: Array[String]): Unit = { val stu = new Stu println(stu.getName()) println(stu.name) } }
|
类型的判断与转换
我们经常要在代码中进行类型的判断和类型的转换。在Java中,我们可以使用instanceof关键字、以及(类型)object来实现,在scala中如何实现呢?
scala中对象提供isInstanceOf和asInstanceOf方法。
- isInstanceOf判断对象是否为指定类的对象
- asInstanceOf将对象转换为指定类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| object Demo17 { class per class Stu extends per
def main(args: Array[String]): Unit = { val stu = new Stu if(stu.isInstanceOf[Stu]){ stu.asInstanceOf[Stu] print(stu) }else{ print("不是stu类型") } } }
|
getClass和classOf
isInstanceOf 只能判断出对象是否为指定类以及其子类的对象,而不能精确的判断出,对象就是指定类的对象。如果要求精确地判断出对象就是指定类的对象,那么就只能使用 getClass 和 classOf 。
- p.getClass可以精确获取对象的类型
- classOf[x]可以精确获取类型
- 使用==操作符就可以直接比较
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| object Demo18 { class Per() class Stu extends Per
def main(args: Array[String]): Unit = { val stu:Per=new Stu
if(stu.isInstanceOf[Per]){ println("stu是一个per类型") }else{ println("stu 不是一个per类型") }
if(stu.getClass == classOf[Per]){ println("stu是一个per类型") }else{ println("stu 不是一个per类型") }
if(stu.getClass == classOf[Stu]){ println("stu是一个STU类型") }else{ println("stu 不是一个stu类型") }
} }
|
调用父类的constructor
实例化子类对象,必须要调用父类的构造器,在scala中,只能在子类的主构造器
中调用父类的构造器
步骤:
- 创建一个Person类,编写带有一个可变的name字段的主构造器
- 创建一个Student类,继承自Person类
- 编写带有一个name参数、clazz班级字段的主构造器
- 调用父类的构造器
- 创建main方法,创建Student对象实例,并打印它的姓名、班级
1 2 3 4 5 6 7 8 9 10
| class Person5(var name:String) // 直接在父类的类名后面调用父类构造器 class Student5(name:String, var clazz:String) extends Person5(name)
object Main5 { def main(args: Array[String]): Unit = { val s1 = new Student5("张三", "三年二班") println(s"${s1.name} - ${s1.clazz}") } }
|
抽象类
如果类的某个成员在当前类中的定义是不包含完整的,它就是一个抽象类
不完整定义有两种情况:
- 方法没有方法体
- 变量没有初始化
没有方法体的方法称为抽象方法,没有初始化的变量称为抽象字段。定义抽象类和Java一样,在类前面加上abstract关键字就可以了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| object Demo19 { abstract class Sop(){ def m():Double } class z(l:Double ) extends Sop{ override def m:Double = l*l } class Cyc(r:Double) extends Sop{ override def m:Double = Math.PI*r*r } class Ju(l:Double,k:Double) extends Sop{ override def m: Double = l*k }
def main(args: Array[String]): Unit = { val z = new z(12.3) val cyc = new Cyc(2) val ju = new Ju(1,3.0) println(z.m) println(cyc.m) println(ju.m) } }
|
抽象字段:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| abstract class Person6 { val WHO_AM_I:String }
class Student6 extends Person6 { override val WHO_AM_I: String = "学生" }
class Policeman6 extends Person6 { override val WHO_AM_I: String = "警察" }
object Main6 { def main(args: Array[String]): Unit = { val p1 = new Student6 val p2 = new Policeman6
println(p1.WHO_AM_I) println(p2.WHO_AM_I) } }
|
匿名内部类
匿名内部类是没有名称的子类,直接用来创建实例对象。Spark的源代码中有大量使用到匿名内部类。
1 2 3 4 5 6 7 8 9 10 11 12
| object Demo20 { abstract class Per(){ def say() }
def main(args: Array[String]): Unit = { val per = new Per { override def say(): Unit = println("nicai") } per.say() } }
|
特质(trait)
scala中没有接口的概念 替代的就是特质
- 特质是scala中代码复用的基础单元
- 它可以将方法和字段定义封装起来,然后添加到类中
- 与类继承不一样的是,类继承要求每个类都只能继承
一个
超类,而一个类可以添加任意数量
的特质。
- 特质的定义和抽象类的定义很像,但它是使用
trait
关键字
用法一 作为接口使用
- 使用
extends
来继承trait(scala不论是类还是特质,都是使用extends关键字)
- 如果要继承多个trait,则使用
with
关键字
继承单个特质
1 2 3 4 5 6 7 8 9 10 11 12 13
| object Demo21 { trait Loger{ def pr(msg:String) } class LogerE extends Loger{ override def pr(msg: String): Unit = print(msg) }
def main(args: Array[String]): Unit = { var e:Loger=new LogerE() e.pr("nicai") } }
|
继承多个特质
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| object Demo22 { trait D1{ def ms(msg:String) } trait D2{ def ms():String } class D3 extends D1 with D2{ override def ms(msg: String): Unit = println(msg)
override def ms(): String = "nicai" }
def main(args: Array[String]): Unit = { val d = new D3 d.ms("ni") println(d.ms()) } }
|
object 继承trait
1 2 3 4 5 6 7 8 9 10 11 12
| object Demo23 { trait D1{ def m(msg:String) } object D2 extends D1{ override def m(msg: String): Unit = print(msg) }
def main(args: Array[String]): Unit = { D2.m("nicai") } }
|
- 在trait中可以定义抽象方法,不写方法体就是抽象方法
- 和继承类一样,使用extends来继承trait
- 继承多个trait,使用with关键字
- 单例对象也可以继承trait
特质 定义具体的方法
和类一样,trait中还可以定义具体的方法。·
1 2 3 4 5 6 7 8 9 10 11 12 13
| object Demo24 { trait D{ def add(msg:String) = println(msg) } class D2 extends D{ def add2()= add("nicai") }
def main(args: Array[String]): Unit = { val d = new D2 d.add2() } }
|
trait中定义具体的字段和抽象字段
- 在trait中,可以混合使用具体方法和抽象方法
- 使用具体方法依赖于抽象方法,而抽象方法可以放到继承trait的子类中实现,这种设计方式也称为模板模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| object Demo25 { trait D{ val s = new SimpleDateFormat("yyyy-MM-dd") var TYPE:String def pr(msg:String) } class D2 extends D{ override var TYPE: String = "控制台消息"
override def pr(msg: String): Unit = println(s"${TYPE}:${s.format(new Date)}:${msg}") }
def main(args: Array[String]): Unit = { val d = new D2 d.pr("nnicai") } }
|
使用trait实现模板模式
在特质中,具体方法依赖于抽象方法,而抽象 方法可以放在继承trait中的子类中实现,这种方式为模板设计模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| object Demo26 { trait Logger{ def log(msg:String) def info(msg:String) = log(msg) def exce(msg:String) = log(msg) def erro(msg:String) = log(msg) } class LogE extends Logger{ override def log(msg: String): Unit = println(msg) }
def main(args: Array[String]): Unit = { val e = new LogE e.info("info") e.exce("exec") e.erro("erro") } }
|
对象混入trait
- trait还可以混入到
实例对象
中,给对象实例添加额外的行为
- 只有混入了trait的对象才具有trait中的方法,其他的类对象不具有trait中的行为
- 使用with将trait混入到实例对象中
语法:
1
| var /val da=new 类 with 特质
|
1 2 3 4 5 6 7 8 9 10 11
| object Demo27 { trait Logger{ def log()=println("nicai") } class Aa
def main(args: Array[String]): Unit = { val aa = new Aa with Logger aa.log() } }
|
trait 实现调用链模式
类继承了多个trait后,可以依次调用多个trait中的同一个方法,只要让多个trait中的同一个方法在最后都依次执行super关键字即可。类中调用多个tait中都有这个方法时,首先会从最右边的trait方法开始执行,然后依次往左执行,形成一个调用链条。
如支付连 等
说明 : 一个子类 继承多个父trait 且还有祖父trait 则 会先执行自己的 在执行从右到左的父trait(继承顺序相反),最后执行祖父trait
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| object Demo28 { trait Zf{ def log(data:String ) = println("祖父") } trait Login extends Zf{ override def log(data: String): Unit ={ println("父1") super.log(data) } } trait Handle extends Zf{ override def log(data: String): Unit = { println("父2") super.log(data) } }
class Ser() extends Handle with Login { override def log(data: String): Unit = { println("子类") super.log(data) }
}
def main(args: Array[String]): Unit = { val ser = new Ser() ser.log("nn") } }
|
trait的构造机制
- trait也有构造代码,但和类不一样,特质不能有构造器参数
- 每个特质只有**
一个无参数
**的构造器。
- 一个类继承另一个类、以及多个trait,当创建该类的实例时,它的构造顺序如下:
- 执行父类的构造器
从左到右
依次执行trait的构造器
- 如果trait有父trait,先构造父trait,如果多个trait有同样的父trait,则只初始化一次
- 执行子类构造器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| class Person_One { println("执行Person构造器!") } trait Logger_One { println("执行Logger构造器!") } trait MyLogger_One extends Logger_One { println("执行MyLogger构造器!") } trait TimeLogger_One extends Logger_One { println("执行TimeLogger构造器!") } class Student_One extends Person_One with MyLogger_One with TimeLogger_One { println("执行Student构造器!") } object exe_one { def main(args: Array[String]): Unit = { val student = new Student_One } }
|
trait继承class
- trait也可以继承class 会把class中的成员都继承下来
- 这个class就会成为所有该trait子类的超级父类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class MyUtil { def printMsg(msg: String) = println(msg) } trait Logger_Two extends MyUtil { def log(msg: String) = this.printMsg("log: " + msg) } class Person_Three(val name: String) extends Logger_Two { def sayHello { this.log("Hi, I'm " + this.name) this.printMsg("Hello, I'm " + this.name) } } object Person_Three{ def main(args: Array[String]) { val p=new Person_Three("Tom") p.sayHello
} }
|