概述
Scala⽤⼀种简洁的⾼级语⾔将 ⾯向对象 和 函数式编程 结合在⼀起。传统业务开发- 领域模型设计(⾯向对象开发); ⼤数据开发 - 数据集计算模型-(函数式编程)。 函数编程 强调的是程序对数据的运⾏算能⼒。在 ⾯向对象计算数据 的时候采取代码不动移动数据.在 函数式编程计算 的时候数据不动代码动。
Scala是⼀⻔多范式的编程语⾔,同时⽀持⾯向对象和⾯向函数编程⻛格。它以⼀种优雅的⽅式解决现实问题。虽然它是强静态类型的编程语⾔,但是它强⼤的类型推断能⼒,使其看起来就像是⼀个动态编程语⾔⼀样。Scala语⾔最终会被翻译成java字节码⽂件,可以⽆缝的和JVM集成,并且可以使⽤Scala调⽤java的代码库。除了Scala编程语⾔⾃身的特性以外,⽬前⽐较流⾏的Spark计算框架也是使⽤Scala语⾔编写。Spark 和 Scala 能够紧密集成,例如 使⽤Scala语⾔操作⼤数据集合的时候,⽤户可以像是在操作本地数据集那样简单操作Spark上的分布式数据集-RDD(这个概念是Spark 批处理的核⼼术语),继⽽简化⼤数据集的处理难度,简化开发步骤。
参考:https://docs.scala-lang.org/tour/tour-of-scala.html
基本操作
变量
Scala是⼀种 纯粹 的⾯向对象编程的语⾔。Scala语⾔中没有原始数据类型,这⼀点和Java语⾔不同,在Scala中⼀切皆对象。以下是Scala语⾔中常⻅类型和类型间的继承关系。
在Java中常⻅的基本类型在Scala中都被剔除了,Scala将值类型和引⽤类型分离。所有的数值变量类型都是 AnyVal的⼦类,这些变量的值都有字⾯值。对于⼀些对象类型的变量都是 AnyRef的⼦类。对于 AnyRef、类下的变量(除String类型),⼀般不允许直接赋值字⾯量,都需要借助 new关键创建。
变量声明
Scala语⾔是⼀种可以做类型⾃动推断的强类型的编程语⾔。变量的类型可通过编译器在编译的时候推断出最终类型。因此Scala中声明⼀个变量主需要告知编译器该变量的值是常量还是变量,例如:例如声明⼀个变量使⽤var即可,如果声明的是⼀个常量使⽤val关键字。因此Scala中变量声明的语法如下:
var|val 变量名字[:类型]=变量值[:类型]
var a = 1
var c:Int=100
var d = 100:Int
var d = 100:Int
注意:Scala虽然可以做类型⾃动推断,类型⼀旦确定,不允许更改-强类型编程语⾔。
注意: val 表示声明常量,声明常量后,数值不能更改。
总结 :所有AnyVal类型的⼦类在声明字⾯值的时候,系统⼀般都可以⾃动推断出字⾯量类型,所以⼀般情况下可以省略类型的声明。
数值转换 √
① 数据类型兼容,且由⼩到⼤可以直接赋值。
var a:Byte = 100
var c = 111
c = a
结果:c变为100
② 由⼤到⼩需要强转,不强转会报错。
③ 强转的方法
a = c.asInstanceOf[Byte]
注意:能够实现数值转换的,要求类型必须是
兼容的
。
var age = "21"
var i = 100
i = age
i = age.asInstanceOf[Int]
String转换为AnyVal类型 √
String类型转换成Int类型
var sum = "123"
var i = 111
i = sum.toInt
i: Int = 123
数组
① 声明⼀个Int类型的数组
② 获取数组的值
③ 遍历⼀个数组
until 表示[0,5)
to 表示[0,5]
for(i <- 0 until a.length ) println(a(i))
for(i <- 0 to a.length-1 ) println(a(i))
a.foreach(item=> println(item))//细化的
元组 √
由若⼲个指定类型的元素构成⼀个只读的复合变量(变量的属性不可更改),称为元组类型。
① 声明元组
var a = (1,2,3,4,5)
a = (2,"zly",21,true)
② 元组数据访问
注意:元组中元素都是只读的,⼀般有值类型和String类型构成。在Scala⼀个Tuple(元组)最多能够有22个成员。元组中的元素都是val类型不允许修改(只读),但是元组变量可以修改。
Unit类型
Scala中将void作为保留关键字,使⽤Unit作为void替换品,在Scala中Unit表示什么也没有的对象。因此字⾯量 ()
分⽀循环
① if条件分⽀
语法:
if(条件){
}else if(条件){ }
...
else{ }
例子1:
var age = 21
if(age < 18){
print(s"你是少年,你的年龄是:${age}")
}else if(age < 30){
print(s"你是⻘年,你的年龄是:${age}")
}else{
println(s"你是中年,你的年龄:${age}")
}
例子2:
注意:根据age参数计算msg的取值,可以看出在Scala语法中 if分⽀ 可以作为变量的赋值语句。可以将分⽀中返回的结果作为返回值,在利⽤分⽀做赋值的时候,不可以给return关键字,系统会⾃动将代码块最后⼀⾏的结果作为返回值。
var message= if(age<18){
s"你是少年,你的年龄:${age}"
}else if(age<30){
s"你是⻘年,你的年龄:${age}"
}else{
s"你是中年,你的年龄:${age}"
}
println(s"最终结果:$message")
② while、do-while
语法:
while(条件){
//循环体
}
do{
//循环体
}while(条件)
案例:
var i = 5
while(i > 0){
println(s"当前i的值是:${i}")
i = i-1
}
do{
println(s"当前i的值:${i}")
i = i-1
}while(i > 0)
③ Breaks
val break = new Breaks
var a = 5
do{
print("\t"+a)
a -= 1
if(a <= 3){
break.break()
}
}while(a > 0)
④ for循环-重点 √
1)迭代遍历数组,item表示a数组中的元素
for(item <- a) println(item)
2)下标遍历数组,i表示a数组中的下标
for(i <- 0 until a.length ) println(a(i))
for(i <- 0 to a.length-1 ) println(a(i))
a.foreach(item=> println(item))//细化的
3) for可使⽤多个循环因⼦
案例:
for(i <- 1 to 9){
for(j <- 1 to i){
print(s"$i * $j = "+(i*j)+"\t")
if(i==j) println()
}
}
等价于
for(i <- 1 to 9;j <- 1 to i){
print(s"$i * $j = "+(i*j)+"\t")
if(i==j) println()
}
4)for和if的使⽤
案例:
for(i<- 0 to 10;if(i%2==0)){
print(i+"\t")
}
5)for和yield关键字实现元素的提取,创建⼦集(重点)
““案例:””
var a = Array(3,2,9,4,6);
for(item <- a) yield print(item*item+"\t")
match-case(模式匹配)
在Scala中剔除java中 switch-case 语句,提供了 match-case 替代⽅案,该⽅案不仅仅可以按照值匹配,还可以按照类型、以及值得结构(数组匹配、元组匹配、case-class匹配等。
① 值匹配
var a=Array(1,2,3,4)
//表示[0,3),i为a中集合的元素
var i=a(new Random().nextInt(4))
var res= i match {
case 1 => "one"
case 2 => "two"
case 3 => "three"
case default => null
}
println(res)
② 类型匹配
var a=Array(100,"张三",true,new Date())
var i=a(new Random().nextInt(4))
var res= i match {
case x:Int => s"age:${x}"
case x:String => s"name:${x}"
case x:Boolean => s"sex:${x}"
case _ => "啥都不是"
}
println(res)
注意:1) _ 表示默认匹配等价 default 关键字
2)var a=Array(100,“张三”,true,127d) 表示 Array[Any] = Array(100, 张三, true, 127.0)的数组
3)元组单独属性不可更改,但是元组变量是可以修改的。
数组的单独元素可以更改,但类型必须和一定记得数组类型相同,数组变量不能更改。
函数(⽅法) √
语法:
def 函数名(参数:参数类型,...):[返回值类型] = {
//⽅法实现
}
① 标准函数
案例:
def sum(x:Int,y:Int):Int = {
return x+y }
或者
def sum02(x:Int,y:Int)= {
x+y }
可以省略return关键字以及返回值类型,系统⼀般可以⾃动推断,并将代码块最后⼀⾏作为返回值。
② 可变⻓参数
def sum(x:Int*) ={
var total = 0
for(i <- x){
total += i
}
//最后一行作为返回值
total
}
或者
def sum(x:Int*)= {
x.sum
}
注意:可变⻓参数必须放置在最后
③ 命名参数
def sayHello(msg:String,name:String):String = {
s"${msg}~ ${name}"
}
print(sayHello("hello","张三"))
print(sayHello(name = "zly",msg = "hello"))
③ 参数默认值
def sayHello(msg:String,name:String="zly"):String = {
s"${msg}~ ${name}"
}
print(sayHello("hello"))
④ 内嵌函数
def factorial(x:Int):Int= {
if(x>0){
x*factorial(x-1)
}else{
1
}
}
def factorial(x:Int):Int={
def mulit(i:Int):Int={
if(i>1){
i*mulit(i-1)
}else{
1
}
}
mulit(x) }
println(factorial(5))
⑤ 柯⾥化(Currying) √
在计算机科学中,柯⾥化(Currying)是把接受多个参数的函数变换成接受⼀个单⼀参数的函数,并且返回接受余下的参数且返回结果的新函数的技术。
案例:
1)
def sum02(x:Int,y:Int)= {
x+y }
等价于
def sum04(x:Int)(y:Int):Int= {
x+y }
println(sum1(1)(2))
2)s由接收两个参数的函数变成接收一个参数的函数,新的函数只接受占位符。
def sum1(x:Int)(y:Int):Int={
x+y
}
//返回一个新函数,_为占位符,
var s = sum1(1)(_) //部分应⽤函数
println(s(4))
结果:5
3)sum1 _表示两个参数都不给值
def t1(x:Int)(y:Int):Int={
x+y
}
var t2 = t1 _
var t3 =t2(3)
print(t3(3))
⑥ 匿名函数|⽅法 √
匿名函数:把一个函数以变量形式表示。
只有参数列表和返回值类型,没有⽅法名,通常使⽤匿名函数形式去声明⼀个 函数式变量 。
案例:
1)
//定义一个函数
def sum01(x:Int,y:Int):Int={
x+y
}
//定义函数的最高境界,用变量去表示一个函数
//函数的类型 = 函数的值
//变量名:函数式变量=>返回值 = 函数的参数=>返回值x+y
var sum:(Int,Int)=>Int = (x,y)=>x+y
//等同于,因为可以做自动类型推断,所以参数返回值类型可以省略
var sum1 = (x:Int,y:Int) =>x+y
结果: sum1(1,2)=3
总结:通过上述变换可以推导出 函数可以转换为变量,因此可以使⽤var或者val修饰函数式变量,⼜因为函数式变量是函数,所以可以使⽤def修饰。
2)将一个标准函数直接转换成一个匿名函数
sum01 _
**结果:**返回 res2: (Int, Int) => Int =
3)
var f = (x:Int,y:Int)=>x+y
def method(x:Int,y:Int,f:(Int,Int)=>Int) : Int = {
f(x,y)
}
print(method(2,4,f))
//数据不动
var r = method(2,4,_:(Int,Int)=>Int)
r((x,y)=>x+y)
4)
def method(op:(Int,Int)=>Int)(x:Int,y:Int)={
op(x,y) }
val result01 = method((x,y)=>x+y)(1,2)
val result02 = method((x,y)=>x*y)(1,2)
println(result01)
println(result02)
Class & object
由于Scala没有静态⽅法和静态类,通过object去定义静态⽅法或者静态对象。当object和Class放在⼀个⽂件中时候称该object为当前Class的伴⽣对象。
单例类
单例类使⽤object修饰,所有声明在object中的⽅法都是静态⽅法。
object HelloUtil {
def sayHello(name:String):Unit={
println("hello~ "+name)
}
}
注意:单例对象在创建对象时不能使用new
def main(args: Array[String]): Unit = {
val hello1 = HelloUtil
val hello2 = HelloUtil
println(hello1 == hello2) //true,因为单例对象
}
类
class User {//不写()也默认存在构造器,里头啥也没有,需要血this.
var id:Int =_
var name:String = _
var age:Int = _
def this(id:Int,name:String,age:Int){
this() #拓展构造器第一行要写默认构造器
this.id=id
this.name=name
this.age=age
}
}
##调用类
var u1 = new User01()
var u2 = new User01()
println(u1 == u2) #false ,他俩是独立对象,指向地址不同
u1.id=1
u1.name="zhangsan"
u1.sex=true
println(s"u1\tid:${u1.id},name:${u1.name},sex:${u1.sex}")
必须要求在扩展构造⽅法的第⼀⾏显式调⽤this(),其中 _ 表示参数赋值为默认值,复杂对象默认值为null,数值型的默认值为0。
① 类上声明(默认构造器)
注意:不写()也默认存在构造器,里头啥也没有,⼀旦类上声明了参数,在使⽤this声明其他的构造器的时候,必须在第⼀⾏调⽤类上的构造器。
class User(var id:Int,var name:String,var age:Int) {
}
② def this定义拓展构造器
类似于java中的构造方法
class User02(var id:Int,var name:String,var sex:Boolean) {//默认构造器
def this(age:Int){
this()
this.age=age
}
def this(id:Int,name:String){//扩展构造器,可以添加默认值
this(id,name,false) //第一行必须调用默认构造器,不在需用写this.
println("-----------")
}
}
结果:
var u4 = new User02(2,"wangwu")
println(s"u4\tid:${u4.id},name:${u4.name},sex:${u4.sex}")
2,wangwu,false
伴⽣对象
如果类和object在⼀个scala源⽂件中,则称为object User 是class User的伴⽣对象。使⽤伴⽣对象可以⽅便的创建类的对象,只需要覆盖对应的apply⽅法。创建时,他们的类名需要一致。
class User03 {
var id:Int=_
var name:String = _
def this(id:Int,name:String){
this()
this.id=id
this.name=name
}
}
object User03{
// unapply 工厂方法 解码对象 - 了解
def unapply(user: User03): Option[(Int, String)] = {
Some((user.id,user.name))
}
//apply 工厂方法
def apply(id:Int,name:String): User03 = new User03(id,name)
}
结果:
var u5=User03 //单例对象,object
var u6=new User03 //类的对象,class
var u8=User03.apply(4,"win7")//调用半生对象的 apply 工厂方法创建类的对象
var u9=User03(4,"win7")//简略写法
println(u8==u9)//false,都是通过new出来的
var User03(id,name) = u7//unapply,把id和name属性从u7中解码出来
例子:
#创建数组对象,类型int,大小为5
var a = new Array[int](5)
#Array对象有个伴生对象。调用工厂方法创建对象
var a = Array[int](1,2,3,4,5)
注意:⼀个伴⽣对象中只能有⼀个unapply⽅法,这不同于apply⽅法,因为apply⽅法可以有多个。
抽象类
定义:
① 抽象类中必须有抽象方法,同时也可以有非抽象方法,既可以有方法的具体实现,继承抽象父类的子类中。
② 如果子类没有实现抽象父类中的抽象方法,那么这个子类也必须声明为抽象的,即只要类中有抽象的方法那么这个类就一定是抽象类,但是抽象类中的方法不一定都是抽象方法,只是至少有一个是抽象方法即可。
③ 抽象类不能直接通过new去实例化一个对象,那它就是不能实例化,要获取抽象类的对象, 需要先用一个类继承抽象类, 然后去实例化子类。也可以用匿名内部类,在抽象类中创建一个匿名的子类,继承抽象类,通过特殊的语法实例化子类的对象 。
abstract class Animal(name:String) {
def eat():Unit={
println("animal can eat...")
}
def sleep():String #抽象方法,只有声明没有实现
}
Trait(接⼝)
trait Speakable {# 接口中可以有多个方法
def speek():Unit
}
trait Flyable{
def fly():Unit # java8之后(包括8)可以有方法的声明和实现
}
继承&实现 √
写⼀个Dog类继承Animal并且实现Speakable特质。
注意:需要覆盖Animal类中的name参数,在Dog上也声明name参数,可以将Animal的名字覆盖掉。
with 后接接口类,如果实现多个特质,需要加多个with
class Dog(name:String) extends Animal(name:String) with Speakable {
override def sleep(): String = {
"i'm a dog I sleep 8 hours"
}
override def speek(): Unit = {
println("wang wang ~~")
}
override def eat(): Unit = {
println("啃⻣头")
}
}
object Dog{
def apply(name: String): Dog = new Dog(name)
}
结果:
val dog = new dog("小花")
dog.fly()
dog.eat()
dog.sleep()
dog.speak()
Trait动态植⼊
假如说现在有⼀个Bird继承⾃Animal,在使⽤的时候会发现该类需要具备Flyable的功能,可以写法如下:
class Bird(var name:String) extends Animal(name :String) {
override def sleep(): String = {
"bird is sleeping"
}
# 覆盖父类的方法
override def eat():Unit={
"小鸟吃虫子"
}
}
var b=new Bird("麻雀") with Flyable{
override def fly(): Unit = {
println("⼩⻦会⻜!")
}
}
注意:
① 加不var,变量只能在内部支持,加了之后,可以在外部支持。
② 在覆盖有实现的⽅法必须添加 overwrite。
③ ⼀个类只能继承⼀个类,可以with多个trait。
self
等价于this关键字,在this出现混淆的时候使⽤self给this关键字起别名。
class User {
self => //必须写在类的第一行,给this取别名,等价于 self:User
var id:Int = _
var name:String = _
var age:Int = _
def this( id:Int, name:String, age:Int){
this() //只能替换this.,这个不能替换
self.id=id
self.name=name
self.age=age
}
}
Trait强制混合
以下案例就是要求所有Flyable的⼦类必须实现Bird接⼝。
trait Flyable2 {
selfType:Bird => //强制要求所有该Flyable2特质实现类,必须是Bird
//this:Bird
def fly():Unit={
//可以将当前对象看做是bird
selfType.sleep()
selfType.eat()
}
}
class Parrot(name:String) extends Bird (name:String ) with Flyable2 {
}
Case class √
case class就像常规类,case class适⽤于对不可变数据进⾏建模。
case class MsmMessage(msg:String) extends Message
case class EmailMessage(msg:String) extends Message
val msmMessage1 = new MsmMessage("短信消息")
val msmMessage2 = new MsmMessage("短信消息")
val msmMessage3 = MsmMessage("短信消息") //样例类 在使用可以省略new关键字
println(msmMessage1 == msmMessage2) //true 比较是内容
var emailMessage2=emailMessage1.copy(msg = "xxxxx")#通过copy方式创建
println(emailMessage2)
注意:
① 与普通的class不同的是,CaseClass创建的对象 == ⽐较的是对象的内容,其次CaseClass的所有属性都是只读,不允许修改。通常⽤于只读数据的建模。可以简单的使⽤copy来实现两个对象间的值得传递。
② case class之间不存在继承关系。
可⻅性-了解
private
① 修饰属性、⽅法
class Student02 {
private var id:Int=_
var name:String=_
def this(id:Int,name:String){
this()
this.id=id
this.name=name
}
private def sayHello(): String ={
s"hello $name"
}
override def toString: String = {
s"$id\t$name"
}
}
object Student02{
def main(args: Array[String]): Unit = {
val stu = new Student02(3,"zhaoliu")
stu.id=4
println(stu.sayHello())
println(stu)
}
}
特点: 该类内部和伴⽣对象可以访问,其他类均不可以访问。
② 修饰扩展构造
class Student03 {
var id:Int=_
var name:String=_
private def this(id:Int,name:String){
this()
this.id=id
this.name=name
}
}
特点: 表示该扩展构造⽅法只能对伴⽣对象可⻅
③ 修饰默认构造
private class Student03 {
var id:Int=_
var name:String=_
private def this(id:Int,name:String){
this()
this.id=id
this.name=name
}
}
特点: 仅仅只能在本类的内部,以及伴⽣对象内部使⽤,其他类不可⻅
protected
① 修饰属性、⽅法
class Student04 {
protected var id:Int=_
var name:String=_
def this(id:Int,name:String){
this()
this.id=id
this.name=name
}
protected def sayHello(): String ={
s"hello $name"
}
override def toString: String = {
s"$id\t$name"
}
}
特点: 针对 本类、以及伴⽣对象可⻅、⼦类可⻅
② 修饰扩展构造
class Student05 {
protected var id:Int=_
var name:String=_
protected def this(id:Int,name:String){
this()
this.id=id
this.name=name
}
}
特点: 对本类以及该类的伴⽣对象可⻅、⼦类可⻅
③ 修饰默认构造
protected class Student05 {
protected var id:Int=_
var name:String=_
protected def this(id:Int,name:String){
this()
this.id=id
this.name=name
}
}
特点: 该类在其他类中可以使⽤,构造仅仅只对本类以及伴⽣对象可⻅。
this限定
可以和private或者protected联合使⽤,可以讲可⻅性缩⼩为本类的内部,去除伴⽣对象。
class Student04 {
protected[this] var id:Int=1 }
object Student04{
def main(args: Array[String]): Unit = {
var st=new Student04
st.id =10//error
}
}
class SmallStudent04 extends Student04 {
def sayHello()={
println(s"${id}")
}
}
包限定
class Student05 {
private[demo12] var id:Int=10
}
class SmallStudent05 extends Student05 {
def sayHello():Unit={
println(s"${id}")
}
}
当使⽤包限定的时候,就打破了private|protected的语义,全部以包为准
final限定(掌握)
① 修饰类:最终类,不可以被继承
② 修饰⽅法:不能被覆盖
③ 修饰属性:只能修饰属性,不可以修饰局部变量(var),scala已经提供了val表示常量,当final修饰成员变量,属性不可以被遮盖。( scala中只⽤val的成员变量才可以被覆盖 )
sealed(密封|闭包) √
Trait和class可以标记为 sealed ,这意味着必须在同⼀源⽂件中声明所有⼦类型这样就确保了所有的⼦类型都是已知的。通常应⽤在case-class声明。
sealed trait Speekable {
def speek():Unit
}
class Xxx1 extends Speekable{
override def speek(): Unit = {
println("--------")
}
}
class Xxx2 extends Speekable{
override def speek(): Unit = {
println("--------")
}
}
}
lazy加载 √
Scala中使⽤关键字lazy来定义惰性变量,实现延迟加载(懒加载)。 惰性变量只能是不可变变量,并且只有在调⽤惰性变量时,才会去实例化这个变量。
def getName():String={
println("----getName-----")
"zhangsan"
}
lazy val name:String=getName()
println("++++")
结果:++++
隐式注入 √
① 隐式值
object CustomImplicits {
//声明隐式值
implicit val a:Int= 1 }
object TestImplicits {
def main(args: Array[String]): Unit = {
//引⼊隐式值
import CustomImplicits._
//获取上下⽂中的⼀个Int 隐式值,要求类型唯⼀
var b:Int= implicitly[Int]
print(s"b值:${b}")
}
}
implicit 声明隐式值, implicitly 获取隐式值
② 隐式注⼊
object CustomImplicits {
//声明隐式值
implicit val a:Int= 1
implicit val msg:String="哈喽" }
object TestImplicits {
def main(args: Array[String]): Unit = {
//引⼊隐式值
import CustomImplicits._
sayHello("张三")
sayHello("lisi")("Hello") #自己声明的值优先使用
}
//柯⾥化
def sayHello(name:String)(implicit msg:String):Unit={
println(s"${msg} ~ ${name}")
}
}
要求上下⽂环境中,必须有⼀个String类型隐式值,系统会⾃动注⼊
③ 隐式转换(把不兼容改为兼容)
object CustomImplicits {
//声明隐式转换方法
implicit def s2d(s:String):Date={
println("-------转换了------")
val sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
sdf.parse(s)
}
}
object TestImplicits {
def main(args: Array[String]): Unit = {
//引⼊隐式值
import CustomImplicits._
showMeTime(new Date())
// String --> Date
showMeTime("2018-12-13 10:30:00") //等价 showMeTime(s2d("2018-12-13 10:30:00"))
}
def showMeTime(date:Date):Unit={
println("北京时间:"+date.toLocaleString)
}
}
④ 隐式增强(把不可能变成可能)
object CustomImplicits {
//声明隐式增强,在类上使用
implicit class PigImplicits(pig:Pig){
def fly():Unit={
println(s"${pig.name} 会⻜了 !")
}
def speek():Unit={
println(s"${pig.name} mu mu~")
}
}
}
object TestImplicits {
def main(args: Array[String]): Unit = {
//引⼊隐式值
import CustomImplicits._
val pig1 = new Pig("猪坚强") with Flyable{
override def fly(): Unit = {# with覆盖接口中的内容
println(s"${name} 会⻜ ~")
}
}
pig1.eat()
pig1.sleep()
pig1.fly() //实现接口的⽅法,存在优先级
pig1.speek()//隐式增强
val pig = new Pig("佩奇")
pig.eat()
pig.sleep()
pig.fly()//隐式增强
pig.speek()//隐式增强
}
异常处理
① Java:区分已检查(必须处理)和未检查(可选)异常、捕获有顺序限制,由⼩->⼤
public class JavaExceptions {
public static void main(String[] args) {
try {
throw new IOException("我⾃⼰抛出的~");
} catch (IOException e) { //由精准--> 模糊
e.printStackTrace();
}catch (ArithmeticException e){
e.printStackTrace();
}
catch (Throwable e){
e.printStackTrace();
} finally {
System.out.println("最终执⾏");
}
}
}
② Scala:不区分已检查和未检查异常,捕获按照写case 进⾏匹配,先匹配类型,类型不一致继续找,直到找到匹配的
object ScalaExceptions {
def main(args: Array[String]): Unit = {
try {
throw new IOException("我⾃⼰抛出的~")
}catch {
case e:ArithmeticException =>{
println("ArithmeticException")
e.printStackTrace()
}
case e: Throwable =>{
println("Throwable")
e.printStackTrace()
}
case e: IOException =>{
println("IOException")
e.printStackTrace()
}
} finally {
System.out.println("最终执⾏")
}
}
}
泛型
① <: 上边界限定
只能包括本类或者子类
//传的类必须是Dog或者Dog的⼦类 上边界限定
def keepDog[T <: Dog](t:T):Unit={# 在方法上使用
println(t) }
def main(args: Array[String]): Unit = {
val animal = new Animal("原始动物")
val dog = new Dog("⼤⻩狗")
val smallDog = new SmallDog("⼩狗狗")
keepDog(dog)
keepDog(smallDog)
keepDog(animal)//错误
}
② >: 下边界限定
只能包括本类或者父类
//只能饲养Dog或者Dog的⽗类 下边界限定 bug
def keepAnimal[T >: Dog](t:T):Unit={
println(t) }
def main(args: Array[String]): Unit = {
val animal = new Animal("原始动物")
val dog = new Dog("⼤⻩狗")
val smallDog = new SmallDog("⼩狗狗")
keepAnimal(dog)
keepAnimal(animal)
keepAnimal(smallDog)//本不应该成功!这⾥我们理解为是个Bug
}
有bug,需要这么设定
//只允许是Dog或者Dog的⽗类
trait Keeper[T >: Dog] {# 在类上使用泛型
def keep(t:T):Unit={
println(t)
}
}
def main(args: Array[String]): Unit = {
val animal = new Animal("原始动物")
val dog = new Dog("⼤⻩狗")
val smallDog = new SmallDog("⼩狗狗")
val k1= new Keeper[Dog] {
override def keep(t: Dog): Unit = {
println(t)
}
}
val k2= new Keeper[Animal] {
override def keep(t: Animal): Unit = {
println(t)
}
}
val k3= new Keeper[SmallDog] { //错误
override def keep(t: SmallDog): Unit = {
println(t)
}
}
}
③ <%视图限定
class SmallDog(name:String) extends Dog (name:String){
override def speek(): Unit = {
println(s"$name ⼩狗叫~")
}
}
//可以将T看做是⼩狗
def keeperSmallDog[T <% SmallDog](t:T):Unit={
t.speek()
}
object MyImlipcits {
//定义⼀个String -> SmallDog转换
implicit def s2sd(name:String):SmallDog={
new SmallDog(name)
}
}
def main(args: Array[String]): Unit = {
keeperSmallDog(new SmallDog("⼩花花"))
import MyImlipcits._
keeperSmallDog("佩奇") }
④ T:A 上下⽂绑定
表示上下⽂中环境中必须存在这种A[T] 隐式值,否则程序编译出错.这样可以在上下⽂中还没有隐式值得时候确保⽅法能编译成功。
注意:发生类型不匹配的函数调用时, scala会尝试进行类型隐式转换;首先优先进行函数参数的类型转换,如果可以转换, 那么就完成函数的执行; 否则尝试去对函数调用对象的类型进行转换; 如果两个尝试都失败了,就会报方法不存在或者类型不匹配的错误;
⑤ +A协变
//管理T或者T的⽗类
trait Manager[+T] {}
var m1=new Manager[Animal] {}
var m2=new Manager[Dog] {}
var m3=new Manager[SmallDog] {}
m1=m2
m2=m3
m2=m1//错误
将⼦类的泛型引⽤赋值给⽗类。
⑥ -A逆变
//管理T或者T的⽗类
trait Manager[-T] {}
var m1=new Manager[Animal] {}
var m2=new Manager[Dog] {}
var m3=new Manager[SmallDog] {}
m1=m2//错误
m2=m3//错误
m2=m1
m3=m1
将⽗类的泛型引⽤赋值给⼦类。
⑦ A不变
trait Manager[T] {}
var m1=new Manager[Animal] {}
var m2=new Manager[Dog] {}
var m3=new Manager[SmallDog] {}
m1=m2//错误
m2=m3//错误
m2=m1//错误
m3=m1//错误