Scala
Scala combines object-oriented and functional programming in one concise, high-level language. Scala’s static types help avoid bugs in complex applications, and its JVM and JavaScript runtimes let you build high-performance systems with easy access to huge ecosystems of libraries.
给大家推荐一个在线的scala文档网站:https://static.runoob.com/download/Scala%E8%AF%AD%E8%A8%80%E8%A7%84%E8%8C%83.pdf
1、Scala基本的程序结构说明 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 object HelloScala { def main (args: Array [String ]):Unit = { println("hello, scala 世界!" ) } }
2、Scala 的数据类型一览图
Any
是所有类的根类型,即所有类的父类(基类)
在 Scala
中类分为两个大的类型分支(AnyVal
[值类型,即可以理解成就是 java 的基本数据类型 ],AnyRef
类型)
在 AnyVal
虽然叫值类型,但是仍然是类(对象)
在 Scala
中有两个特别的类型(Null
), 还有一个是 Nothing
Null
类型只有一个实例 null
, 他是 bottom class
,是 AnyRef
的子类.
Nothing
类型是所有类的子类, 它的价值是在于因为它是所有类的子类,就可以将 Nothing
类型的对象返回给任意的变量或者方法,比如案例
1 2 3 def f1 ():Nothing = { throw new Exception ("异常发生" )}
在 Scala
中仍然遵守 低精度的数据自动的转成高精度的数据类型。
在 Scala
中,Unit
类型比较特殊,这个类型也只有一个实例 ()
Scala数据类型列表
数据类型
描述
Byte
8位有符号补码整数。数值区间为 -128 到 127
Short
16位有符号补码整数。数值区间为 -32768 到 32767
Int
32位有符号补码整数。数值区间为 -2147483648 到 2147483647
Long
64位有符号补码整数。数值区间为 -9223372036854775808 到 9223372036854775807
Float
32 位 IEEE 754标准的单精度浮点数
Double
64 位 IEEE 754标准的双精度浮点数
Char
16位无符号Unicode字符, 区间值为 U+0000 到 U+FFFF
String
字符序列
Boolean
true或false
Unit
表示无值,和其他语言中void等同。用作不返回任何结果的方法的结果类型。Unit只有一个实例值,写成()。
Null
null 可以赋值给任意引用类型(AnyRef),但是不能赋值给值类型
Nothing
Nothing类型在Scala的类层级的最低端;它是任何其他类型的子类型。
Any
Any是所有其他类的超类
AnyRef
AnyRef类是Scala里所有引用类(reference class)的基类
3、函数式编程 函数式编程基础
函数定义/声明
函数运行机制
递归 [推荐编程者递归来解决问题, 算法基础, 邮差问题,最短路径,背包问题, 迷宫,回溯 ]
过程
惰性函数和异常
函数式编程高级
值函数(函数字面量)
高阶函数
闭包
应用函数
柯里化函数,抽象控制..
在 Scala 当中,函数是一等公民,像变量一样,既可以作为函数的参数使用,也可以将函数赋值给一个变量. ,函数的创建不用依赖于类或者对象,而在 Java 当中,函数的创建则要依赖于类、抽象类或者接口。
惰性函数
当函数返回值被声明为 lazy 时,函数的执行将被推迟,直到我们首次对此取值,该函数才会执行。这种函数我们称之为惰性函数,在 Java 的某些框架代码中称之为懒加载(延迟加载),Java中没有原生方法。
lazy
不能修饰 var
类型的变量
不但是在调用函数时,加了 lazy
,会导致函数的执行被推迟,我们在声明一个变量时,如果给声明了 lazy
,那么变量值得分配也会推迟。 比如 lazy val i = 10
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 object Demo1 { def main (args: Array [String ]): Unit = { lazy val res = sum(1 , 2 , 3 , 4 , 6 ) println(res) } def sum (args: Int *): Int = { var res = 0 for (n <- args) { res += n } res } }
异常 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 def main (args: Array [String ]): Unit = { try { var n = 10 /0 }catch { case ex:ArithmeticException =>{ println("异常:" +ex.getMessage) } case exception: Exception =>{ println(exception.getMessage) } }finally { printf("完成" ) } }
Scala 提供了 throws
关键字来声明异常。可以使用方法定义声明异常。 它向调用者函数提供了此方法可能引发此异常的信息。 它有助于调用函数处理并将该代码包含在 try-catch 块中,以避免程序异常终止。在 scala 中,可以使用@throws
注释来声明异常
1 2 3 4 @throws (classOf[ArithmeticException ])def function1 (): Unit ={ var n = 10 /0 }
4、Scala 构造器的基本语法 1 2 3 4 5 6 7 class 类名 ( 形参列表 ) { def this ( 形参列表) { } def this ( 形参列表) { } }
属性高级
Scala 类的主构造器的形参未用任何修饰符修饰
,那么这个参数是局部变量。
如果参数使用 val
关键字声明,那么 Scala 会将参数作为类的私有的只读属性使用
如果参数使用 var
关键字声明,那么那么 Scala 会将参数作为类的成员属性使用,并会提供属性对应的xxx()[类似 getter]/xxx_$eq()[类似 setter]方法,即这时的成员属性是私有的,但是可读写。
Bean 属性
JavaBeans 规范定义了 Java 的属性是像 getXxx()和 setXxx()的方法。许多 Java 工具(框架)都依赖这个命名习惯。为了 Java 的互操作性。将 Scala 字段加@BeanProperty
时,这样会自动生成规范的 setXxx/getXxx 方法。这时可以使用 对象.setXxx() 和 对象.getXxx() 来调用属性。
给某个属性加入@BeanPropetry
注解后,会生成 getXXX 和 setXXX 的方法
并且对 原来底层自动生成类似 xxx(),xxx_$eq()
1 2 3 4 5 6 7 8 9 10 11 12 13 class Cat { @BeanProperty var name: String = "" @BeanProperty var age: Int = 0 @BeanProperty var color: String = "" def this (name: String ) { this () this .name = name } override def toString = s"Cat($name , $age , $color )" }
5、Scala 中包的可见性和访问修饰符的使用
当属性访问权限为默认时,从底层看属性是 private 的,但是因为提供了 xxx_$eq()[类似 setter]/xxx()[类似getter] 方法,因此从使用效果看是任何地方都可以访问)。
当方法访问权限为默认时,默认为 public 访问权限。
private
为私有权限,只在类的内部和伴生对象中可用。
protected
为受保护权限,scala 中受保护权限比 Java 中更严格 , 只能子类访问,问同包无法访问 (编译器从语法层面控制)。
在 scala 中没有 public 关键字 ,即不能用 public 显式的修饰属性和方法。
scala 设计者将访问的方式分成三大类: (1) 处处可以访问 public (2) 子类和伴生对象能访问 protected (3) 本类和伴生对象访问 private
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Dog {import scala.beans.BeanProperty @BeanProperty var name : String = "" } import java.util.{ HashMap =>JavaHashMap , List }import java.util.{ HashMap =>_, _}
6、继承 重写方法 Scala 明确规定, 重写一个非抽象方法需要用 override 修饰符,调用超类的方法使用 super 关键字
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 def main (args: Array [String ]): Unit = { var student = new student student.name = "michong" student.num = "123123" student.getInfo() } class person { var name: String = _ def getInfo (): Unit = { println("姓名:" + name) } } class student extends person { var num: String = _ override def getInfo (): Unit = { super .getInfo() println("学号:" + num) } }
Scala 中类型检查和转换 基本介绍
要测试某个对象是否属于某个给定的类,可以用 isInstanceOf
方法。用 asInstanceOf
方法将引用转换为子类的引用。classOf
获取对象的类名。
classOf[String]就如同 Java 的 String.class 。
obj.isInstanceOf[T]就如同 Java 的 obj instanceof T 判断 obj 是不是 T 类型。
obj.asInstanceOf[T]就如同 Java 的(T)obj 将 obj 强转成 T 类型
1 2 3 var student:person = new studentstudent.name = "michong" student.asInstanceOf[student].num = "123123"
var 重写抽象的 var 属性小结
一个属性没有初始化,那么这个属性就是抽象属性
抽象属性在编译成字节码文件时,属性并不会声明,但是会自动生成抽象方法,所以类必须声明为抽象类
如果是覆写一个父类的抽象属性,那么 override
关键字可省略 [原因:父类的抽象属性,生成的是抽象方法,因此就不涉及到方法重写的概念,因此 override
可省略]
Scala 抽象类
抽象类不能被实例
抽象类不一定要包含 abstract 方法。也就是说,抽象类可以没有 abstract 方法
一旦类包含了抽象方法或者抽象属性,则这个类必须声明为 abstract
抽象方法不能有主体,不允许使用 abstract 修饰。
如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法和抽象属性,除非它自己也声明为 abstract类。【案例演示+反编译】
抽象方法和抽象属性不能使用 private、final 来修饰,因为这些关键字都是和重写/实现相违背的。
抽象类中可以有实现的方法.
子类重写抽象方法不需要 override,写上也不会错.
1 2 3 4 5 6 7 abstract class person { var name:String } class student extends person { var name:String ="" }
匿名子类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 def main (args: Array [String ]): Unit = { val p = new person { override var name: String = "123" override def info (): Unit = { println("test" ) } } p.info() } abstract class person { var name:String def info () } }
7、伴生对象
Scala 中伴生对象采用 object
关键字声明,伴生对象中声明的全是 “静态
“内容,可以通过伴生对象名称直接调用。
伴生对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。
伴生对象中的属性和方法都可以通过伴生对象名直接调用访问
从语法角度来讲,所谓的伴生对象其实就是类的静态方法和静态变量的集合
从技术角度来讲,scala 还是没有生成静态的内容,只不过是将伴生对象生成了一个新的类,实现属性和方法的调用。[反编译看源码]
从底层原理看,伴生对象实现静态特性是依赖于 public static final MODULE$
实现的。
伴生对象的声明应该和伴生类的声明在同一个源码文件中(如果不在同一个文件中会运行错误!),但是如果没有伴生类,也就没有所谓的伴生对象了,所以放在哪里就无所谓了。
如果 class A 独立存在,那么 A 就是一个类, 如果 object A 独立存在,那么 A 就是一个”静态”性质的对象[即类对象], 在 object A 中声明的属性和方法可以通过 A.属性 和 A.方法 来实现调用
案例
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 35 36 37 38 package cn.buildworld.scala.day2object demo1 { def main (args: Array [String ]): Unit = { val p1 = new Person ("michong" ) val p2 = new Person ("qjzxzxd" ) val p3 = new Person ("米虫" ) Person .joinGroup(p1) Person .joinGroup(p2) Person .joinGroup(p3) Person .showInfo() } class Person (pname: String ) { var name: String = pname } object Person { var num: Int = 0 def joinGroup (person: Person ): Unit = { num += 1 println(person.name + "--加入组织" ) } def showInfo (): Unit ={ println("当前" +num+"人" ) } def say (): Unit = { println("Say Hi" ) } } }
8、特质(trait) trait的声明 1 2 3 4 5 6 trait 名 特质名 {trait 体 体 } 1 ) trait 命名 一般首字母大写 .2 ) 在 scala 中,java 中的接口可以当做特质使用
trait使用
1 2 3 4 #没有父类 class 类名 extends 质 特质 1 with 质 特质 2 with 质 特质 3 ..#有父类 class 类名 extends 父类 with 质 特质 1 with 质 特质 2 with
带有特质的对象,动态混入
除了可以在类声明时继承特质以外,还可以在构建对象时混入特质,扩展目标类的功能【反编译看动态混入本质】
此种方式也可以应用于对抽象类功能进行扩展
动态混入是 Scala 特有的方式(java 没有动态混入),可在不修改类声明/定义的情况下,扩展类的功能,非常的灵活,耦合性低 。
动态混入可以在不影响原有的继承关系的基础上,给指定的类扩展功能。[如何理解]
抽象类中有 抽象的方法,如何动态混入特质->可以,在创建实例时,实现抽象方法即可
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 35 36 37 38 39 package cn.buildworld.scala.day2object demo2 { def main (args: Array [String ]): Unit = { val c = new C c.getConnect("root" ,"123456" ) val b = new B with Trait1 { override def getConnect (user: String , pwd: String ): Unit = { println("用户登录信息:user: " +user+" pwd: " +pwd) } } b.getConnect("root" ,"123456" ) } trait Trait1 { def getConnect (user:String ,pwd:String ): Unit } class A {} class B extends A {} class C extends A with Trait1 { override def getConnect (user: String , pwd: String ): Unit = { println("用户登录信息:user: " +user+" pwd: " +pwd) } } class D {} class E extends D with Trait1 { override def getConnect (user: String , pwd: String ): Unit = {} } }
特质构造顺序
特质也是有构造器的,构造器中的内容由“字段的初始化”和一些其他语句构成。具体实现请参考“特质叠加”
调用当前类的超类构造器
第一个特质的父特质构造器
第一个特质构造器
第二个特质构造器的父特质构造器, 如果已经执行过,就不再执行
第二个特质构造器
…….重复 4,5 的步骤(如果有第 3 个,第 4 个特质)
第 2 种特质构造顺序(在构建对象时,动态混入特质)
调用当前类的超类构造
当前类构造
第一个特质构造器的父特质构造器
第一个特质构造器.
第二个特质构造器的父特质构造器, 如果已经执行过,就不再执行
第二个特质构造器
……重复 5,6 的步骤(如果有第 3 个,第 4 个特质)
自身类型 1 2 3 4 5 6 7 8 9 10 11 #在Logger 中已经可以使用Exception 中的相关的方法了 trait Logger { this :Exception => def log ()={ println(getMessage) } } #使用MyLogger 时先继承Exception 类 class MyLogger extends Exception with Logger {}
嵌套
方式 1 内部类如果想要访问外部类的属性,可以通过外部类对象访问。即:访问方式:外部类名.this.属性名
方式 2 内部类如果想要访问外部类的属性,也可以通过外部类别名访问(推荐)。即:访问方式:外部类名别名.属性名 【外部类名.this 等价 外部类名别名】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class AAA { myOuter=> class InnerAAA { def test (innerAAA: InnerAAA ): Unit ={ println(innerAAA) println(myOuter.name) println(myOuter.sal) } } var name:String =_ private var sal:Double =_ }
9、隐式转换 隐式值
隐式值也叫隐式变量,将某个形参变量标记为 implicit,所以编译器会在方法省略隐式参数的情况下去搜索作用域内的隐式值作为缺省参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package cn.buildworld.scala.day2object demo5 { def main (args: Array [String ]): Unit = { implicit val str1:String = "michong" def hello (implicit name:String ): Unit ={ println(name) } hello } }
隐式类
其所带的 构造参数有且只能有一个
隐式类必须被定义在“类”或“伴生对象”或“包对象”里,即隐式类不能是顶级的(top-level objects)
隐式类不能是 case class(case class 在后续介绍样例类)
作用域内不能有与之相同名称的标识符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package cn.buildworld.scala.day2object demo5 { def main (args: Array [String ]): Unit = { val db = new MySql db.add() } implicit class DB (val m :MySql ) { def add (): Unit ={ println("添加" ) } } class MySql {} }
隐式函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package cn.buildworld.scala.day2object demo4 { def main (args: Array [String ]): Unit = { implicit def addDelete (mySql: MySql ):DB ={ new DB } var db = new MySql db.delete() } class MySql {} class DB { def delete (): Unit ={ println("删除数据!!!" ) } } }
隐式解析机制
首先会在 当前代码作用域下查找隐式实体(隐式方法、隐式类、隐式对象)。(一般是这种情况)
如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找。类型的作用域是指与该类型相关联的全部伴生模块,一个隐式实体的类型 T 它的查找范围如下( 第二种情况范围广且复杂在使用时,应当尽量避免出现):
a) 如果 T 被定义为 T with A with B with C,那么 A,B,C 都是 T 的部分,在 T 的隐式解析过程中,它们的伴生对象都会被搜索。
b) 如果 T 是参数化类型,那么类型参数和与类型参数相关联的部分都算作 T 的部分,比如 List[String]的隐式搜索会搜索 List 的伴生对象和 String 的伴生对象。
c) 如果 T 是一个单例类型 p.T,即 T 是属于某个 p 对象内,那么这个 p 对象也会被搜索。
d) 如果 T 是个类型注入 S#T,那么 S 和 T 都会被搜索。