变量
表达式
略
声明变量
语法:
var 变量名:类型 = 初始值
scala中的类型有:
Double Float Long Int Short Byte Unit Char Boolean yourClass
- 所有的值类型都是Scala.*包下的
- String类型延续了
java.lang.String
- 值类型的默认类型:Int(整数),Double(小数)
- 类型支持支持自动推断
var a:Int = 0
var b:Double = 0.0
var c:String = ""
//简写(自动推断类型)
var d = 0
var e = 0.0
var f = ""
常量
只允许赋值一次
语法:
val 常量名:类型= 值
小知识:
创建对象时,建议使用常量
val
val a = 10
//常量只允许赋值一次,如果继续进行赋值,会GG
类型转换
简单来说就是自动类型提升
Byte > Short > Int > Long > Float > Double
Char
小知识:
Char类型的值可以转换为Int类型
var a:Byte = 10
var b:Short = a
var c:Int = b
//Char类型的值可以转换为Int类型
var q:Char = '96'
//可以用大类型指向大类型 byte > int 但是不能反过来
var d:Long = 10
//这么玩会出事的
var e:Byte = d
运算符
略
Scala层次类型结构
- Any是所有类的超类型,也就是顶级父类,顶级类型等。它定义了一些如:equals,toString方法等、Any有俩直接子类,AnyVal和AnyRef
AnyVal
代表值类型。有9个预定义的非空的值类型分别是:Double
、Float
、Long
、Int
、Short
、Byte
、Char
、Unit
和Boolean
。Unit
(类似于java中的void
)是不带任何意义的值类型,它仅有一个实例可以像这样声明:()
。所有的函数必须有返回,所以说有时候Unit
也是有用的返回类型。AnyRef
代表引用类型。所有非值类型都被定义为引用类型。在Scala中,每个用户自定义的类型都是AnyRef
的子类型。如果Scala被应用在Java的运行环境中,AnyRef
相当于java.lang.Object
。Nothing
是所有类型的子类型,也称为底部类型。没有一个值是Nothing
类型的。它的用途之一是给出非正常终止的信号,如抛出异常、程序退出或者一个无限循环(可以理解为它是一个不对值进行定义的表达式的类型,或者是一个不能正常返回的方法)。Null
是所有引用类型的子类型(即AnyRef
的任意子类型)。它有一个单例值由关键字null
所定义。Null
主要是使得Scala满足和其他JVM语言的互操作性,但是几乎不应该在Scala代码中使用。我们将在后面的章节中介绍null
的替代方案。
小知识:
在scala的命令窗口,如果想写入多行代码,可以使用
:paste
模式,多行代码写入完成后,按ctrl+D
键退出执行
分支语句
if语句
if(布尔表达式){
//代码题
}
if … else
if(布尔条件){
//为true执行
}else{
//为false执行
}
if … else if …else语句
if(布尔条件1){
// true 执行
}else if(布尔条件2){
// true 执行
}else{
// 以上条件不匹配 执行
}
总结:
- scala中所有的表达式都是有值得,如果表达式的最后一行内容为打印语句,则表达式的值为
() Unit
类型- scala中代码块的最后一行内容为表达式的值
- scala中代码块中有多行内容时,必须使用大括号
{}
包裹起来,如果只有一行内容,{}
可以省略
匹配模式(相当于java中的switch)
小知识:
- scala中并没有提供switch语句,但是提供了一个功能远超于switch语句的语法,叫做
模式匹配
语法
变量名 match{
case 值 => 处理内容
case 值 => 处理内容
case _ => 处理内容
}小知识:
_
类似于default- 模式匹配不存在匹配穿透问题
var x:Int = 10
x match{
case 5 => println("x的值为:" + x)
case 10 => println("x的值为:" + x)
case 15 => println("x的值为:" + x)
case _ => println("你个铁憨憨,值没有一个是对的")
}
循环
while循环
语法:
while(循环条件){
// 循环体
}
var x = 10
while(x < 0){
println(x)
x -= 1
}
//小小小demo,1到100的和
var n = 1
var sum = 0
while(n <= 100){
sum += n
n += 1
}
do…while
语法:
do{
// 循环体
}while(循环条件)
// 1~100数值之和
var n = 1
var sum = 0
do{
sum += n
n += 1
}while(n <= 100)
for循环
语法:
for(i <- 0 to 100){
println(i)
}
var sum = 0
for(i <- 0 to 100){
sum += i
}
println(sum)
var sum = 0
for(i <- 0 to 100){ // 数值区间Range(0~100),包含起始和结束数值
sum += i
}
println(sum)
var sum = 0
for(i <- 0 until 100){ // 数值区间Range(0~99),包含起始和不包含结束数值
sum += i
}
println(sum)
// 老生常谈的九九乘法表
for(m <- 1 to 9){
for(n <- 1 to m){
print(n+"*"+m+"="+(m*n) +"\t")
}
println()
}
嵌套for循环
嵌套for语法
多重循环体在for的()中用 ; 分割
// 原来的样子
for(m <- 1 to 9){
for(n <- 1 to m){
print(n+"*"+m+"="+(m*n) +"\t")
}
println()
}
//简化的样子
for(m <- 1 to 9;n <- 1 to m){
if(m != n){
print(n+"*"+m+"="+(m*n) +"\t")
}eles{
println(n+"*"+m+"="+(m*n) +"\t")
}
}
迭代的步长
默认情况下for循环都是每次循环后加一
而通过 by 的设置,可以改变它的默认值
// 统计 1 ~ 100 偶数之和
var sum = 0
for( n <- 0 to 100 by 2){
sum += n
}
println(sum) // 2550
if守卫
在一个1~~100的循环体中,我只想要其中的偶数值
那么,这个时候大多数想的是在循环体中加上if语句
对循环体进行限定,从而达到目标
(别问我为啥不用迭代步长,if用习惯了不行啊)
//正常写法
var sum = 0
for(n <- 1 to 100){
if( n % 2 == 0){
sum += n
}
}
//身为程序员,当然要对自己好一点,能偷的懒绝对要偷
// if守卫简化实现 嘿嘿嘿
var sum = 0
for(n <- 1 to 10 if n % 2 != 0){
sum += n
}
//在来几个,练练手
// n 必须是奇数 并且n不能是5
var sum = 0
for(n <- 1 to 10 if n % 2 != 0 && n != 5){
sum += n
} // 20
// n 是奇数 或者n可以使6
var sum = 0
for(n <- 1 to 10 if n % 2 != 0 || n == 6){
sum += n
} // 31
break
小知识:
java中没有break和continue关键字
import scala.util.control.Breaks._
//所以要想在某些情况下停止循环,只能这么玩。
breakable {
for (n <- 1 to 10) {
if (n == 5) break() // 在scala中如果是一个无参的方法,可以省却(),直接写方法名
println(n)
}
}
yield
yield就是相当于循环体的记事本,保存当前循环体遍历出了那些数据并且返回
也可以作出某些=处理后再返回
val newColle = for(n <- 1 to 10 if n % 2 == 1 && n != 5) yield n*3
函数
函数是一段具有特别功能的代码块
函数三要素:函数名,返回值,参数列表
public static 返回值类型 m1(参数列表){ // 函数体 return xxx; }
函数声明
def 方法名(参数列表) : 返回值类型 = {
方法体
}
//例子
def Text(x:Int,y:Int):Int ={
x + y
}
Demo
package function
object Function01 {
// main 快速生成main函数
def main(args: Array[String]): Unit = {
// 如何调用普通函数
}
def sum(x:Int,y:Int): Int = {
//scala会被函数体中的最后一行代码当成返回值返回(前提返回值类型不是unit的祛疤膏将选)
//或者是方法体的最后一行是一个无法返回的数据,比如一个打印语句
x + y
}
// 无返回值的函数 Unit类型的值为()
def sum1(x: Int, y: Int): Unit = {
println(x + y)
}
//合理偷懒版
//可以省略返回值类型,scala中支持自动类型推导,简单点来说 //就是这个鬼东西满聪明,可以算出你先返回的值类型,所以就不需要你自己写返回值类型
def sum2(x: Int, y: Int){
//println(x + y)
x + y
}
}
总结
- 有返回值的函数,在声明时可以省略
:返回值类型
,scala自动根据类型推导返回值类型 - 无返回值的函数,在声明时可以省略
:返回值类型=
, scala会自动认为是无返回值的函数
可变长参数
在java中如果声明可变长参数`public static void m1(String … args)
在scala中如果声明可变长参数
def 函数名(args:Int*):返回值类型 ={}
object Function02 {
def main(args: Array[String]): Unit = {
println(m1(1, 2, 3, 4, 5)) // 15
println(m1(1, 2, 3, 4, 5, 6, 7)) // 28
println(m2(1, 1,2, 3, 4, 5, 6, 7)) // 29
}
// 声明一个Int类型的可变长参数 如:计算参数列表的整数之和
def m1(args: Int*): Int = { // 数组
var total = 0
for (n <- args) {
total += n
}
total
}
// 注意: 在定义可变长参数时,可变长参数需要放置在函数的参数列表之后
def m2(init: Int, args: Int*) = {
var default = init
for (n <- args) {
default += n
}
default
}
}
参数默认值
scala的函数的参数是可以有默认值的
在使用时如果调用的时候传值,则使用传过来的值进行计算
如果调用时没有传值,这使用默认值
def sum(x: Int, y: Int, z: Int = 10): Int = { // 函数的参数有默认值的`参数名:类型名 = 默认值`
x+y+z
}
带名参数
调用函数时,给指定的参数名的参数赋值
println(sum2(x = 1, z = 2)) // 13 【使用带名参数】
println(sum2(z = 2, x = 1)) // 13 【使用带名参数】 注意:带名参数使用时与函数的参数列表的顺序无关
def sum2(x: Int, y: Int = 10, z: Int): Int = { // 函数的参数有默认值的`参数名:类型名 = 默认值`
x + y + z
}
Lazy值(懒加载)
在scala中,如果一个常量声明成了Lazy,则代表这个常量,会在第一次使用时才会被加载
变量不可以被声明为lazy值,不然报错,,,
初始化
object Function04 {
def main(args: Array[String]): Unit = {
// 语法 lazy 变量或者常量的声明
// 立即进行初始化 ERROR
val source = fromFile("D://abc.txt")
// 懒加载的值
lazy val source2 = fromFile("D://abc.txt")
// 使用时,懒加载的值该初始化了,ERROR
source2.mkString
//mkString这个方法可以先简单的理解为读取文件类容的,并且可以跟根你指定的参数对文件进行划分
}
}
递归函数
类似于java中的递归
个人理解,简单点来说,递归函数就是在函数体里面继续调用自己,向下执行,直到有值返回时在逐一向上
获取结果,最后到最上层返回结果给调用者
// 计算1~10的阶乘
package function
/**
* 递归函数(求1~n阶乘)
*/
object Function05 {
def main(args: Array[String]): Unit = {
println(m1(3)) // 6
println(m2(5))
}
// 求1~n的整数之和
def m1(n: Int): Int = {
var total = 0
if (n > 0) {
total += n + m1(n - 1)
}
total
}
// 第一次递归: n <= 0 total = 0
// 第二次递归: n = 1 total = 0 + 1
// 第三次递归: n = 2 total = 0 + 1 + 2
// 第四次递归: n = 3 total = 0 + 1 + 2 + 3 = 6
def m2(n: Int): Int = {
var total = 1
if (n >= 1) {
total *= n * m2(n - 1) // 1*2*3*4*5 = 120
}
total
}
// 第一次递归:
}
匿名函数
顾名思义:没有函数名的函数,就是匿名函数
语法:参数列表 => 函数体
(x:Int,y:Int) => x + y //个人感觉scala牛逼就是牛逼到这在,写代码正儿八经的高效率
小知识:内置了23个匿名函数对象
Function0~Function22
, 匿名函数的背后对应的是一个函数对象
object Function{
def main(args: Array[String]): Unit = {
var functionTest:Function2[Int,Int,Int] = (i:Int,j:Int) => i + j
println(functionTest(1,2))
val list = List(1, 2, 3, 4, 5)
//一个没有返回值的匿名函数
var functionText2:Function[Int,Unit] = (i:Int) => println(i)
list.foreach(functionText2)
}
}
总结:
- 定义匿名函数时要先确定当匿名函数的参数列表长度是多少
- 知道匿名函数的参数数量后,可以通过Function0~~~Function22来限定参数的长度,并给参数设置泛型
- 泛型的最后一个类型,是返回值的类型,最后一个泛型的类型,决定了当前匿名函数的返回值类型
- 最后记得书写该匿名函数的算法(运算规则)
柯里化(currying)
定义:
柯里化函数指将接受多个参数的函数,改造为接受一个参数的函数,并且返回一个函数对象的过程
个人理解:以下是个人理解的柯里化函数的演算过程
//这一步没啥可说的,看第二部
def sum(x:Int,y:Int) = {
x + y
}
//简化版
def sum2(x:Int) = {
//这里做了使用了一个匿名函数,当sum2方法调用的时候,参数x被赋值,然后进入方法体
//这时,发现匿名函数y没有值,运算无法完成,只能直接返回带一个参数的Function对象
//返回时,这个方法变成了=>Function[Int,Int] = (y:Int) => 1 + y 这个样子
//会等待用户给第二个参数的值,继续计算
(y:Int) => x + y
}
//精简版
def sum3(x:Int)(y:Int) ={
//简单通俗的理解 =>反正都要匿名函数来满足参数唯一的原则
//那我何必不直接把那个匿名函数写到上面呢
//也就是说 def sum3(x:Int)(y:Int) ={} 中(y:Int)不再是参数列表了,而是匿名函数
//然后直接在 ={} 里面规定算法并且做返回(需要返回的话)
//突然感觉scala语言设计的好巧妙有木有
x + y
}
//----结果----------------------------------
println(sum(1,2)) //3
println(sum2(1)) //<function1> 之所以要先知给一个参数,就是想看看返回的是个什么玩意
println(sum2(1)(2)) //3 sum2方法的正确调用
println(sum3(1)(2)) //3
数组
>数组定义: 数据的集合,在内存中表示为一段连续的存储空间
数组的定义
// 语法: val arr = Array[泛型](元素列表)
val arr:Array[Int] = Array[Int](1,2,3,4,5,6,7,8,9)//完整写法
val arr = Array[Int](1, 2, 3, 4, 5)//偷懒写法(别问为啥,问就是scala会自动类型推断)
数组的使用
和java没啥区别,都是用下标
//数组的声明
val arr = Array[Int](1, 2, 3, 4, 5)
//来吧,数组除了遍历,也没别的可玩了
// 遍历方法1
for(n <- 0 until arr.length){
println(arr(n)) // 注意: 获取数组中元素使用的是 数组名(下标)
}
// 遍历方法2
for(n <- arr){
println(n)
}
// 遍历方法3
for(n <- arr.indices){ // indices 获取到的是数组的下标数值区间
println(arr(n))
}
//还有修改数组中的元素
arr(3) = 10
数组排序
冒泡排序。。。
val arr = Array(5,10,2,1,6,7)
//额,模仿java版,,,
for(m <- 0 until arr.length){
for(n <- 0 until arr.length - m -1){
if(arr(n) > arr(n+1)){
val tmp = arr(n)
arr(n) = arr(n+1)
arr(n+1) = tmp
}
}
}
//scala【嵌套 + if守卫】
//scala中,双层for循环是可以写在一起的,if语句也是可以写在循环体中,语法就是这样的,,,
for(m <- 0 until arr.length; n <- 0 until arr.length - m -1 if arr(n) > arr(n+1)){
val tmp = arr(n)
arr(n) = arr(n+1)
arr(n+1) = tmp
}
for( m <- arr){
println(m)
}
可变数组ArrayBuffer
Array 定长数组,一旦声明 数据长度就固定不可变
ArrayBuffer 可变数组,类似于集合,数组会自动扩容
val ab = ArrayBuffer(1, 2, 3, 4, 5)
// 可变数组添加元素
ab += 6 // 相当于 => ab.+=(7) 这里的 += 是一个方法
for(n <- ab){
println(n)
}
Array和ArrayBuffer相互转换
val ab = ArrayBuffer(1, 2, 3, 4, 5)
// arrayBuffer ---> array 可变到定长的转换
val arr = ab.toArray
// array ---> arrayBuffer 定长到可变长的转换
val abf = arr.toBuffer
多维数组
简单理解就是:
一维数组中每个元素就是一个单纯的值了,String,Int,yourClass等等类型的值了
二维数组则是每个元素里面还是一个数组,以此类推,就简称对维数组了
val arr = Array.ofDim[Int](10, 4) // 类似于10行4列的二维表
// 操作
arr(1)(2) = 2
// 获取
println(arr(1)(2))
for (m <- 0 to arr.length - 1) {
for (n <- 0 to arr(m).length - 1) {
print(arr(m)(n) + "\t")
}
println()
}
类
类(class):对象的模板,成员(有什么)和方法(做什么)
简单类
语法:
语法: `class 类名{ // 成员或者方法 }
// 声明一个Animals
class Animals{}
// 创建对象
val animals:Animals = new Animals()
或
val animals:Animals = new Animals
小知识
- 类中提供一个默认的无参的主构造器
- 如果创建对象时调用是无参的构造方法,
()
可以省略
成员/方法
小知识
- scala中成员全部都是私有的(
private
)- ''scala中成员的getter方法名
成员名()
- scala中成员的setter方法名
成员名_$eq(参数)
class ScalaTest{
var id:Int = 0 //scala中,类的成员在被定义时,必须要有初始值
var name:String = ""
var password :String = ""
override def toString: String = "id:" + id + "\t name:" + name + "\t password" + password
}
val scalaTest = new ScalaTest
//声明常量成员,依然是私有的成员,scala自动为成员提供公开的getter【sex()】方法
//声明变量成员, 都是私有的成员,scala自动为成员提供公开的setter【id_$eq | name_$eq】/getter[id() | //name()]方法
scalaTest.id = 1
scalaTest.name = "zs"
scalaTest.password = "123456"
println(scalaTest)
方法
类中的方法和函数的语法一样
object ColorTest {
def main(args: Array[String]): Unit = {
val red: Red = new Red
println(red.display())
println(red)
}
}
class Red {
var name: String = "red"
// 方法
def display(): String = {
this.name
}
override def toString: String ="红色"
}
手动生成get/set方法
语法:
- getter方法 ---->
def 区分成员名() = 成员值
- setter方法 ---->
def 区分成员名_(形参) {成员值 = 形参值}
object PersonTest {
def main(args: Array[String]): Unit = {
val person = new Person
person.setName_("zs")
println(person.getName)
}
}
class Person {
// 自定义name成员的getter和setter方法
var name: String = ""
// 自定义getter
def getName(): String = this.name
// 自定义setter方法
def setName_(newName: String): Unit = this.name = newName
}
@BeanProperty => 手动生成get/set方法偷懒版
此注解加载成员的前面,scala编译时,会自动生产java版本和scala版本的getter/setter方法
object CourseTest {
def main(args: Array[String]): Unit = {
val chinese = new Chinese
chinese.setCourseName("文言文") //java风格的setter方法
chinese.courseName = "四书五经" // scala风格setter方法
val courseName = chinese.courseName
println(courseName)
}
}
class Chinese {
@BeanProperty var courseName: String = "中文"
}
辅助构造器
辅助构造器,类似于java重载的构造方法
小知识:scala的一个类中,可以有一个主构造器和若干个辅助构造器
object FishTest {
def main(args: Array[String]): Unit = {
val fish1 = new Fish() // 调用无参数的主构造器
val fish2 = new Fish("金鱼") // 调用无参数的主构造器
val fish3 = new Fish("金鱼", "黄色") // 调用无参数的主构造器
println(fish1)
println(fish2)
println(fish3)
//----------------------------
/*
金鱼
金鱼 黄色
*/
//----------------------------
}
}
// scala默认提供一个无参的主构造器
class Fish {
var name: String = ""
var color: String = ""
// 辅助的构造器
// 注意:辅助构造器必须以调用一个主构造器或者辅助构造器开始
def this(name: String) {
this()
this.name = name
}
def this(name: String, color: String) {
this(name)
this.color = color
}
override def toString: String = this.name + "\t" + this.color
}
小知识
- 辅助构造器,使用的关键字
this
- 辅助构造器内,第一行必须调用主构造器或者辅助构造器
主构造器
- 声明,主构造器的参数列表直接写在类名之后:
class Fish(var name:String,var color:String){}
- 主构造器的方法体,是整个类,也就是类的
{}
的所有内容,注意主构造器方法体中的成员和方法仅仅是声明,并不会执行
object PandaTest {
def main(args: Array[String]): Unit = {
val p1 = new Panda("小黑","熊猫色")
println()
val p2 = new Panda("小黑","熊猫色")
}
}
// 主构造器
class Panda(var name: String, var color: String) {
var age: Int = 0
// 方法体
println("before")
def sayHello(str: String): String = {
str + name
}
println("after")
}
/*
//java的构造器
class Panda{
private String name;
private String color;
private Int age;
public Panda(String name,String color){
this.name = name;
this.color = color;
}
}*/
对象(object)
代替java中
static
语法的,因为scala没有static
,如果需要在scala的源文件中定义静态的成员和方法,使用object
类型
单例一个对象
不管创建多少次,只存在一个对象实例
总结:
- scala中一个类,类型为object,此类即为单例对象,j简单来说,就是对象(object)是单例的
- 类体中成员和方法,都是静态成员【所有类共享的】
/**
* scala中一个类,类型为object,此类即为单例对象
*/
object SingletonObject {
// 静态属性
var id: Int = 0
// 静态方法
def incrementId(): Int = {
this.id = this.id +1
this.id
}
def main(args: Array[String]): Unit = {
val s1 = SingletonObject
val s2 = SingletonObject
val s3 = new SingletonObject()
val s4 = new SingletonObject()
println(s1 == s2) //true【单例】
println(s3 == s4) //false【多例】
val id1 = s1.incrementId()
val id2 = s2.incrementId()
println(id1+"\t"+id2)
}
}
class SingletonObject {
}
伴生类/伴生对象
在一个scala的源文件中,class的名字和object名字一样,class类就被称为伴生类,object类就称为伴生对象
/**
* 伴生类&伴生对象
*
* 注意:伴生类可以访问伴生对象的私有成员
*
* java中: 普通方法能不能调用静态方法或者成员 【可以】
*/
class Person {
def sayHi(): String = {
"Hi:" + Person.name + "\t" + Person.id
}
}
object Person {
val id: Int = 0
private val name: String = "zs"
def main(args: Array[String]): Unit = {
val person = new Person
println(person.sayHi())
}
}
apply & unapply
apply 方法是object类型中的一个方法,通常作为工厂方法创建伴生类的对象【简化方式】
结论:
** 调用的伴生对象中的apply方法,用以创建伴生类**
class Cat(var name: String, var color: String) {
}
object Cat {
// apply方法创建伴生类
def apply(name: String, color: String): Cat = new Cat(name, color)
def main(args: Array[String]): Unit = {
// 创建Cat对象
val cat = new Cat("小花", "white")
// apply 工厂方法,用以创建类的对象,通常定义在伴生对象中
// 创建伴生类对象时【简化写法】
val cat2 = Cat("小黑","black") // 等价于Cat.apply("小黑","black")
val cat3 = Cat("小黑","black") // 等价于Cat.apply("小黑","black")
println(cat2 == cat3)
}
}
unapply
unapply: 伴生对象中的方法,作用和apply方法相反,接受一个伴生类对象,返回伴生类对象的成员
class Cat(var name: String, var color: String) {
}
object Cat {
// apply方法创建伴生类
def apply(name: String, color: String): Cat = new Cat(name, color)
// 将伴生类对象 拆箱为成员
// Option 【避免出现空指针异常,有值 Some 无值 None】
// - None
// - Some
def unapply(arg: Cat): Option[(String, String)] = {
if (arg == null) None
else {
Some((arg.name, arg.color)) //(name,color) scala元组,可以存放任意类型的list集合 类型与java中的ArrayList[Object]
}
}
def main(args: Array[String]): Unit = {
// 创建Cat对象
val cat = new Cat("小花", "white")
// apply 工厂方法,用以创建类的对象,通常定义在伴生对象中
// 创建伴生类对象时【简化写法】
val cat2 = Cat("小黑", "black") // 等价于Cat.apply("小黑","black")
val cat3 = Cat("小黑", "black") // 等价于Cat.apply("小黑","black")
println(cat2 == cat3)
// unapply方法应用
val Cat((name, color)) = cat3 // Cat.unapply(cat3) = Some(cat3.name,cat3.color)
// val name = cat3.name
// val color = cat3.color
println(name + "\t" + color)
// ================================================
}
}
继承
Java中的继承,子类继承父类等价于继承了父类成员和方法,使用的关键字
extends
,单继承
基本语法
class Animals {
// 私有成员,公开的getter/setter方法,
var name: String = ""
def sayHi(): String = {
"Hello:" + name
}
}
class Dog extends Animals {
}
object Dog {
def main(args: Array[String]): Unit = {
val dog = new Dog
dog.name = "小黑" // 父类的setter
println(dog.sayHi()) // 父类的sayHi
}
}
小知识
- scala的继承同样使用的是
extends
- scala的子类同样可以继承父类中的成员和方法
方法覆盖
覆盖:父类定义的方法无法子类需求时,在子类中定义相同的方法,在调用时,以子类优先
关键字
override
,不可省略
class Person {
def run(): Unit = {
println("人在奔跑!")
}
}
class SmallStudent extends Person {
// 父类方法的覆盖 `override`
override def run(): Unit = println("小学生在学走路!")
}
object SmallStudent {
def main(args: Array[String]): Unit = {
val student = new SmallStudent
student.run() // 小学生在学走路!
}
}
类型检查和转换
java中检查类型是:
isInstanceof
if(dog isInstanceof Animals) 这个样子scala中是:
- 判断类型是否兼容:
isInstanceOf[类型]
- 类型转换语法:
asInstanceOf[另外一种类型]
val bool = student.isInstanceOf[Person]
println(bool) // true
val person = new Person
val bool2 = person.isInstanceOf[SmallStudent]
println(bool2) // false
//-------------------------------------------
// 将小学生的类型 赋值父类型
val person2: Person = student.asInstanceOf[Person]
// 因为person2的真实类型是SmallStudent
val student2 = person2.asInstanceOf[SmallStudent]
用父类有参构造
/*
color父类没有无参的主构造器,只有一个有一个参数的主构造器
scala中没有`Super()`语法,子类必须得调用父类的有参构造
*/
class Color(var name: String) {
println("父类")
}
class Red(name: String) extends Color(name: String) {
println("子类")
}
object Red {
def main(args: Array[String]): Unit = {
val red = new Red("红色")
println(red.name)
}
}
/*
//java中的父类有参构造调用
class Color{
String name;
public Color(String name){
this.name = name;
}
}
class Red extends Color{
public Red(){
super("red")
}
}
*/
结论:
- 如果调用父类的有参构造方法,需要在extends 父类
(父类的有参构造)
- 子类的主构造器中,必须有父类的成员,并且不需要声明为
val
或者var
重写字段
class A {
var id: Int = 0
val name: String = ""
private var sex: Boolean = false // getter/setter私有的方法
}
class B extends A {
// 子类覆盖父类成员id/name
// 覆盖重写变量 id, 注意:省略了变量的关键字`var`和变量的类型
id = 10
// 覆盖重写常量 name
override val name: String = "BB"
private var sex: Boolean = true // 并不是继承,子类中特有的成员
}
object B {
def apply(): B = new B()
def main(args: Array[String]): Unit = {
val b = B()
println(b.id + "\t" + b.name + "\t" + b.sex) //10 BB true
}
}
总结:
- 如果子类需要覆盖父类中的变量(var),子类中不需要var关键字和变量的类型
- 如果子类需要覆盖父类中的常量(val),子类必须在父类常量的基础之上加上
override
关键字 - 如果父类中的私有成员(private),子类是无法覆盖,原因是:父类中属性和getter/setter方法都是私有的
抽象类
类似于java的抽象类,使用的关键字依然是
abstract
- 抽象类,可能有抽象成员和普通成员
- 子类继承抽象类,必须得实现抽象类的声明方法,如果未实现,子类依然是一个抽象类
- 抽象类只能声明引用而不能创建对象
- 在实现抽象类的抽象方式或者属性时,依然使用的是关键字
override
// 抽象类
abstract class Person2 {
// 抽象字段
val name: String
// 抽象方法
def playGame(): String
}
object Person2Test {
def main(args: Array[String]): Unit = {
//1. 抽象类只能声明引用而不能创建对象
val person1:Person2 = new SmallStudent2
println(person1.name +"\t" +person1.playGame)
val person2:Person2 = new HighStudent2
println(person2.name +"\t" +person2.playGame)
}
}
class SmallStudent2 extends Person2 {
override val name: String = "小学生"
override def playGame(): String = "王者荣耀"
}
abstract class MiddleStudent2 extends Person2 {
override def playGame(): String = "英雄联盟"
}
class HighStudent2 extends Person2 {
override def playGame(): String = "绝地求生"
override val name: String = "高中生"
}
匿名子类
和java中的匿名内部类差不多一样
abstract class A2 {
def m1(): Unit
}
class B2 extends A2 {
def m1(): Unit = {
// 方法体
}
}
// 匿名子类
val a2: A2 = new A2 {
override def m1(): Unit = println("m1")
}
a2.m1()
包可见性
java访问修饰符: public ,default,protected,private
scala访问修饰符: default(public)、protected、private
特质(trait)
类似java中的接口
语法
- 特质的关键字trait
- 特质可以同时用于抽象方法和具体方法
- 无父类 实现特质
extends trait1 with trait2 with trait3 ...
- 有父类 实现特质
extends 父类 with 特质1 with 特质2 ...
/**
* 等价于Java接口
* 1. 公开静态常量
* 2. 公开抽象方法
* 3. JDK8之后,接口中也可以由具体的方法
*/
trait Person {
// 特质中的属性
var name: String
// 特质中的一个抽象方法
def run(): String
}
class Father
class SmallStudent extends Person { //无父类 实现特质extends trait1 with trait2 with trait3 ...
override var name: String = "小学生"
override def run(): String = name + "在走路!"
}
class MiddleStudent extends Father with Person{ //有父类 实现特质extends 父类 with 特质1 with 特质2 ...
override var name: String = "中学生"
override def run(): String = name +"在奔跑!"
}
object PersonTest {
def main(args: Array[String]): Unit = {
val student = new SmallStudent
student.name = "小屁孩"
println(student.run())
}
}
带有具体实现的特质
scala中的特质是可以有具体实现方法的,有点类似有jdk1.8中的接口
package traites
trait Animals {
// 注意:特质中既可以有抽象成员 也可以有具体成员
var name: String = "小动物"
def sleep(): Unit = {
println(name + ":在睡觉!")
}
def eat(): String
}
class Dog extends Animals {
override def eat(): String = "吃饭!"
override def sleep(): Unit = println(name +":sleeping")
}
object Dog {
def main(args: Array[String]): Unit = {
val dog = new Dog
dog.eat()
dog.sleep()
dog.name ="xx"
}
}
小知识:让特质混有具体行为有一个弊端. 当特质改变时, 所有混入该特质的类都必须重新编译
带特质的对象 动态混入(mixin)重点
动态混入(mixin):指在创建单个对象时,可以在不破换对象声明的前提下,扩展对象的功能
在构造单个对象时, 你可以为它添加特质
- 特质可以将对象原本没有的方法与字段加入对象中, 功能扩展
- 无需修改类声明定义,扩展类的功能,灵活简便
- 如果特质和对象改写了同一超类的方法, 则排在右边的先被执行
package traites.mixin
trait Animals {
println("animals")
def eat(): Unit
}
trait Person {
println("person")
def playGame(): Unit
}
trait Student extends Animals {
println("student")
def study(): Unit
//覆盖父类中的eat方法
override def eat(): Unit = println("学生吃的肉和菜")
}
/*//传统写法:在增强 SmallStudent 功能混入两个Trait
class SmallStudent extends Person with Student {
override def playGame(): Unit = ???
override def study(): Unit = ???
override def eat(): Unit = ???
}*/
class SmallStudent { // 在不破坏类的声明要求下,扩展功能(吃、玩游戏、学习能力)
println("smallStudent")
}
object ScalaTest {
def main(args: Array[String]): Unit = {
// 注意:特殊语法,可以在创建伴生类时混入特质[抽象类],增强伴生类的功能,这样的语法就称为动态混入(mixin)
// 在有动态混入语法时:对象创建的顺序 从左到右进行初始化
val smallStudent = new SmallStudent with Person with Student {
override def playGame(): Unit = println("打游戏了")
override def study(): Unit = println("学习使我快乐")
}
smallStudent.playGame()
smallStudent.study()
// 如果特质和对象改写了同一超类的方法, 则排在右边的先被执行
smallStudent.eat() //因为Student特质中已经重写了eat方法,所以可以直接使用
}
}
总结
- 特殊语法,可以在创建伴生类时混入特质[抽象类],增强伴生类的功能,这样的语法就称为动态混入(mixin)
- 在有动态混入语法时:对象创建的顺序 从左到右进行初始化
- 如果特质和对象改写了同一超类的方法, 则排在右边的先被执行
- 简单来说,动态混入就是在 new 一个类时,手抖的添加上相对于的特质,已完成某些需求,而不是在该类申明的时候加上特质,这样就保证了该类的纯净,不会有接口污染的情况出现
this别名
给当前类(this)起一个别名(可以随便定制,建议
self
看scala的源码的话很发现很多源码开头都有一句:self =>
这句相当于给this
起了一个别名为self
。self
不是关键字,可以用除了this
外的任何名字命名(除关键字)。就下面的代码,在Student内部,可以用this
指代当前对象,也可以用self
指代,两者是等价的。
class AA {
self => //一般情况下,this就代表当前类,而现在self单标 AA类
var id: Int = 0
def getId(): Boolean = {
//self.id => AA类
this.id == self.id
}
}
object AA {
def main(args: Array[String]): Unit = {
val aa = new AA
val bool = aa.getId()
println(bool) //true
}
}
//-------------------------------------------------------
class Outer {
outer => //outer => Outer类
val v1 = "here"
class Inner {
inner => //inner => Inner类
val v1 = "-------------"
println(outer.v1) // 用outer表示外部类,相当于Outer.this
println(inner.v1)
}
}
object Outer {
def main(args: Array[String]): Unit = {
val outer = new Outer()
val inner = new outer.Inner
/**
* here
* -------------
*/
}
}
self-type(自动类型)
强制混入
trait Cat {
val name: String
}
trait SmallCat{
// 自类型:强制混入,表示当前特质类必须混入类型Cat
self:Cat =>
def sayHi():String = {
self.name // 必须混入Cat
}
}
class SmallFamilyCat extends SmallCat with Cat{
override val name: String = "小花花"
}
object SmallFamilyCat{
def main(args: Array[String]): Unit = {
val cat = new SmallFamilyCat
val result = cat.sayHi()
println(result)
}
}
高阶函数
匿名函数
上面写过,,,
传统写法:
def sum(x:Int,y:Int):Int = x+y
简化写法:
val f2:Function2[Int,Int,Int] = (x:Int,y:Int) => x+y
柯里化函数
上面也写过,,,
传统写法:
def sum(x:Int) = (y:Int) => x+y
简化写法:
def sum(x:Int)(y:Int) = x+y
函数对象
Scala默认提供了23个函数对象,Function0~Function22
object FunctionObject {
def main(args: Array[String]): Unit = {
val f2 = (x: Int, y: Int) => x + y
/* // 创建Function2函数对象
val f2:Function2[Int,Int,Int] = new Function2[Int, Int, Int] {
override def apply(v1: Int, v2: Int): Int = v1 + v2
}
*/
val sum = f2(1, 2)
println(sum)
}
}
函数对象做为参数
小知识
Scala的函数对象可以作为值进行传递,原因函数也是对象
package function
object HighLevelFunction01 {
def main(args: Array[String]): Unit = {
val f2 = (x: Int, y: Int) => x + y
sum(1,2,f2)
}
/**
*
* @param x
* @param y
* @param f2 函数对象
*/
def sum(x: Int, y: Int, f2: (Int, Int) => Int): Unit = {
println(f2(x,y)) // 结果 3
}
}
参数类型推断
老牛b的来了
package function
object HighLevelFunction02 {
def main(args: Array[String]): Unit = {
val arr = Array(1, 2, 3, 4, 5)
arr.foreach((n: Int) => println(n))
// 匿名函数 省略了参数列表的类型,因为匿名函数类型和数组元素类型一致
arr.foreach((n) => println(n))
// 接受单个参数 () 省略
arr.foreach(n => println(n))
// 接受单个参数 n => 省略
arr.foreach(println(_))
// 最终版
arr.foreach(println)
}
}
一个关于高阶函数的小练习
object HighLevelFunction03ForWordCount {
def main(args: Array[String]): Unit = {
val lines = Array("Hello Hello","Hello Hadoop")
lines
.flatMap((line:String) => line.split(" ")) // 将一个内容展开为0~n个内容
.map((word:String) => (word, 1)) // 将一个类型转换为另外一个类型
.groupBy(t2 => t2._1) // 根据元组中的word分组
.map(t2 => (t2._1,t2._2.length))
.foreach(println)
println("-------------------------------")
lines
.flatMap(_.split(" "))
.map((_,1))
.groupBy(_._1)
.map(t2 => (t2._1,t2._2.length))
.filter(! _._1.equals("Hello")) //
.foreach(println)
}
}
函数闭包
闭包指在函数体内,可以访问相应作用域内的任何变量,因为它引用到函数外面定义的变量,定义这个函数的过程是将这个自由变量捕获而构成一个封闭的函数。
我一直以为闭包这个概念只有JavaScript有,没想到scala也有,难道是都有s???
object HighLevelFunction03 {
def main(args: Array[String]): Unit = {
var n = 0
// 函数闭包: 函数对象 + 外部变量 = 闭包
val f1 = (x: Int) => x + n
println(f1(1))
n = 100 // 函数对象闭包后,只保存一个外部变量的引用地址,当外部的变量在发生改变时,会影响到闭包后的结果
println(f1(2))
}
}
总结
- 简单来说,就是 函数对象 + 外部变量 = 闭包
- 其中外部变量可以影响该函数对象的结果
异常处理
scala中的异常处理,和java中的异常处理基本一致。
/**
* 测试scala的异常处理
*/
object Exception01 {
def main(args: Array[String]): Unit = {
// m1()
m2()
println("-----------------------")
}
/**
* 消极处理 throw
*
* 1) scala保留了java异常处理类
* 2) scala不要通过throws声明可能抛出异常
*/
def m1(): Unit = {
// throw new RuntimeException
throw new NullPointerException
println("---------------")
}
/**
* 积极处理 try ... catch...finally
*/
def m2(): Unit = {
try {
var i: Int = 1 / 0
} catch {
// 模式匹配【详解】 匹配的try块中可能抛出异常的类型
case e1: NullPointerException => println("空指针异常")
case e2: RuntimeException => println("运行时异常")
case e3: Exception => println("异常!!!")
case _ => println("其它错误")
}
finally {
println("finally block")
}
}
}
隐式类型转换
隐式转换函数(implicit conversion function)指的是以implicit关键字声明的带有单个参数的函数。这样的函数将被自动应用,将值从一种类型转换为另一种类型。隐式转换函数叫什么名字是无所谓的,因为通常不会由用户手动调用,而是由Scala进行调用。但是如果要使用隐式转换,则需要对隐式转换函数进行导入(import
)。因此通常建议将隐式转换函数的名称命名为“one2one”的形式。
常用使用方式:
- 隐式值
- 隐式参数
- 隐式函数
- 隐式增强
- 隐式类
Scala会考虑如下位置的隐式转换函数:
- 位于源或目标类型的伴生对象中的隐式函数
- 位于当前作用域可以以单个标识符指代的隐式函数
隐式转换在如下三种不同情况下会被考虑:【知道】
- 当表达式类型与预期类型不同时【隐式值的使用】
- 当对象访问一个不存在成员时【隐式增强】
- 当对象调用某个方法,而这个方法的参数声明与传入参数不匹配时【隐式函数】
有三种情况编译器不会尝试使用隐式转换: 【知道】
- 如果代码能够在不使用隐式转换的前提下通过编译,则不会使用隐式转换
- 编译器不会尝试同时执行多个转换
- 存在二义性的转换是错误
隐式值
在声明变量或常量添加implicit关键字
如:`implicit var name:String = “zs”
/**
* 隐式值的使用
*/
object Implicit01 {
def main(args: Array[String]): Unit = {
// 1. 声明
implicit var name:String = "zs"
// implicit var address:String = "ls" //error
implicit val sex:Boolean = true //ok
// 2. 使用
// 注意:
// 1. 在使用隐式值时,通过隐式值的类型获取
// 2. 同类型的隐式值只能有一个
val newValue =implicitly[String]
val newValue2 =implicitly[Boolean]
println(newValue)
println(newValue2)
}
}
小知识:
- 在使用隐式值时,通过隐式值的类型获取
- 同类型的隐式值只能有一个
隐式参数
有一些特殊的方法,含有隐式参数
语法:
def sayHi(implicit name:String):Unit = {}
小知识: 如果方法或者函数含有隐式参数,会自动在作用域寻找隐式值赋予隐式参数
object Implicit02 {
def main(args: Array[String]): Unit = {
implicit val name: String = "zs"
implicit val default: Int = 0
sayHi // 触发隐式参数的匹配
sayHi("ls") // 因为调用时有具体的参数值,并不会触发隐式参数的匹配
println(sum(1)(2)) // 3
println(sum(1)) // 1
println(sum2) // 0 zs
println(sum2(10,"ls")) // 10 ls
}
/**
*
* @param name 隐式参数:在作用域中寻找同类型隐式值 同类型隐式值赋予隐式参数
*/
def sayHi(implicit name: String): Unit = {
println("Hello:" + name)
}
def sum(x: Int)(implicit y: Int): Int = {
x + y
}
def sum2(implicit x: Int, y: String): String = { // x和y都为隐式参数
x + "\t" + y
}
}
隐式函数
将一种类型转换为另一种类型
package implicitconversion
object Implicit03 {
def main(args: Array[String]): Unit = {
sayHi(new Person("zs")) // 正常写法
sayHi("ls")
}
/**
* 隐式函数: 将一种类型转换为另外一种类型
* 注意:隐式函数不是我们调用的,是scala在编译不通过式,会在作用域寻找可以进行转换函数
* @param
*/
implicit def str2Person(str:String):Person={
new Person(str)
}
def sayHi(p: Person): Unit = {
println("Hello:" + p.name)
}
}
class Person(var name: String) {
}
隐式增强
通过隐式转换增强某一个类的功能
/**
* @Author: songbowen
* @Description:
* @Date: Create in 20:46 2019/12/9
*/
object Test1 {
def main(args: Array[String]): Unit = {
val man = new Man("zs") //创建Man对象
/**
* 因为Man类里面没有fiy方法,只有superMam里面有
* 1本来这里是会报错的,
* 2但是这里有一个隐式函数,需要Mam对象当参数,返回值为SupMan(隐式函数scala会自动调用)
* 3这时,我就通过man对象获取到了supMan对象,也就可以调用fiy了,也就不会报错了
*
*/
man.fly
}
// man ---> superMan
implicit def man2SuperMan(man:Man):SuperMan = {
new SuperMan(man.name)
}
}
class Man(var name: String){}
class SuperMan(var name: String) {
def fly = {
println("fly~~~~~~~")
}
}
隐式类
implicit修饰的特殊类,注意不能独立声明,需要定义在一个类\对象的内部
/**
* 隐式类
* 这个和上面的差不多一样,我应该忘不了,忘了回来再翻翻代码。。。
*/
object Implicit05 {
def main(args: Array[String]): Unit = {
val man = new Man2("zs")
man.fly
// 隐式类: 主构造器接受一个类型 转换为隐式类类型 man2 ---> SuperMan2
implicit class SuperMan2(man2: Man2) {
var name: String = ""
def fly = {
this.name = man2.name
println("fly~~~~~~~~")
}
}
}
}
class Man2(var name:String)
隐式转换的导入
总结
- 如果在当前作用域(源文件)有隐式转换,无需导入
- 如果在当前作用域没有隐式转换,需要手动导入,使用语法
import xxx
package implicitconversion
/**
* 隐式转换的使用:
* - 对象中的隐式转换,使用如下的语法 对象名._
* - 类中隐式转换,使用如下的语法: val 引用 = new 类
* import 引用._ 【语法】
*
* 注意:
* 导入的隐式转换不能和当前类的成员产生命名冲突
*/
object Implicit06 {
def main(args: Array[String]): Unit = {
// 对象中的隐式转换,使用如下的语法
import AA._
val n2: Int = implicitly[Int]
println(n2)
val s: Boolean = implicitly[Boolean]
println(s)
// 类中隐式转换,使用如下的语法:
val bb = new BB
import BB._
val str2: String = implicitly[String]
println(str2)
val c2: Char = implicitly[Char]
println(c2)
}
}
object AA {
implicit val n: Int = 10
implicit val sex: Boolean = false
}
class BB {
implicit val str: String = "bb"
implicit val c: Char = 'A'
}
泛型
规定了类或方法的使用类型
泛型类
语法:class[ 泛型类型 ] (主构造器) {
//代码块
}
-8/**
* 泛型类
* T S M K
*/
class Generic01[T, S, K](var name: T, var sex: S) {
var age: K = _ // _相当于通配符,或者默认值
override def toString: String = name + "\t" + sex + "\t" + age
}
object Generic01 {
def main(args: Array[String]): Unit = {
val g1 = new Generic01[String, Boolean, Int]("zs", false)
g1.age = 10
println(g1)
}
}
泛型方法或函数
/**
* 泛型方法或者函数
*
* 貌似也没啥可说的。。。
* 泛型规定的什么类型,参数就用什么类型就好了
*/
object Generic02 {
def main(args: Array[String]): Unit = {
print[Int](Array(1, 2, 3, 4, 5))
// print[Int](Array("a","b","c")) //error
}
def print[T](arr: Array[T]): Unit = {
arr.foreach(println)
}
}
上边界
语法[T <: S] 限定T类型必须是S类型的子类,包含S类型本身
class A
class B extends A
// 上边界 T类型必须A子类型
class C[T <: A](var t: T)
class D
object C{
def main(args: Array[String]): Unit = {
val c1 = new C[A](new A) //A类型本身 ok
val c2 = new C[B](new B) //A类型的子类B ok
// val c3 = new C[D](new D) //A类型的子类B ERROR
}
}
下边界
语法:
[T >: S]
,限定T类型必须S的父类型,包含S类型本身
package generic.lower
class A
class B extends A
// 下边界
class C[T >: B](var t: T)
class D extends B
object C {
def main(args: Array[String]): Unit = {
val c1 = new C[B](new B) // 下边界本身类型 ok
val c2 = new C[A](new A) // A类型是B类型 ok
val c3 = new C[D](new D) // D类型是B类型子类型 不符合下边界语义 ERROR
}
}
视图边界
语法:
[T <% S]
, 运行时尝试将T类型隐式转换为S类型
import java.text.SimpleDateFormat
/**
* 视图边界【限定】
*
* str ---> java.util.Date
*/
class A[T <% java.util.Date](val date: T)
object A {
def main(args: Array[String]): Unit = {
val a = new A[String]("2019-11-20 15:21:30")
println(a.date.getYear +"\t"+a.date.getMonth + "\t" +a.date.getDay)
}
// 提供一个隐式转换函数 str --> java.util.Date
implicit def str2Date(str: String): java.util.Date = {
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(str)
}
}
多重限定
多上界
语法:
[T <: S with K]
表示T类型必须同时是S类型和K类型的子类型
package generic.multiupper
class A
class B extends A
class C extends B
// 多上界 表示:T 类型必须是B类型和A类型的子类型
// 特点:包含最小的上边界B类型、B类型和A类型的子类
class D[T <: B with A](t:T)
class E
object D{
def main(args: Array[String]): Unit = {
val d1 = new D[C](new C) // OK
val d2= new D[B](new B) // OK
// val d3= new D[A](new A) // ERROR
// val d4= new D[E](new E) // ERROR
}
}
多下界
语法:
[T >: S with K]
表示T类型是S类型或者K类型的父类型
class A
class B extends A
class C
class D extends C
// 多下界 表示 T类型必须是B类型或者D类型的父类型
class E[T >: B with D](t: T)
// class F
class F extends D
object E{
def main(args: Array[String]): Unit = {
val e1 = new E[A](new A) //ok
val e2 = new E[D](new D) //ok
val e3 = new E[B](new B) //ok
val e4 = new E[C](new C) //ok
//val e5 = new E[F](new F) //error
}
}
既有上界,又有下界
语法:
[T >: S <: K]
表示T类型的上边界为K类型,下边界为S类型
package generic.upper_lower
class A
class B extends A
class C extends B
// 既有上边界 又下边界
class D[T >: C <: A](var t: T)
class E extends C
object D{
def main(args: Array[String]): Unit = {
val d1 = new D[B](new B) //ok
val d2 = new D[A](new A) //ok
val d3 = new D[C](new C) //ok
// val d4 = new D[E](new E) //error
}
}
协变
语法:
[+T]
如果T类型是T1类型的父类型,则 C[T]类型是C[T1]类型的父类
class A
class B extends A
class C[+T]
C[A] ---> C[B] 父类
//---------------------------------------------------------
class A
class B extends A
class C[+T]
object C {
def main(args: Array[String]): Unit = {
val c1 = new C[B]
// 协变后 C[A] 依然是 C[B] 父类型
val c2: C[A] = c1
}
}
逆变
语法:
[-T]
如果T类型是T1类型的父类型,则 C[T1]类型是C[T]类型的父类
class A
class B extends A
class C[-T]
C[B] ---> C[A] 父类
//-------------------------------------------------------
package generic.nb
class A
class B extends A
// 逆变
class C[-T]
object C{
def main(args: Array[String]): Unit = {
val c1 = new C[A]
val c2:C[B] = c1
}
}
不变
语法:
[T]
,使用方法同泛型类和泛型方法 (这有点扯犊子,,,)
上下文限定
语法:
[T:M]
上下文界定的形式为T:M,其中M是另一个泛型类,它要求必须存在一个类型为M[T]的隐式值。
// 语法:`[T:M]`
//
// 上下文界定的形式为T:M,其中M是另一个泛型类,它要求必须存在一个类型为M[T]的隐式值。
class Person(var name: String, var age: Int)
/**
* T: 泛型类(Ordering)
*
* @tparam T
*/
class CompareUtils[T: Ordering](p1: T, p2: T) {
def compare: Int = {
val ordering = implicitly[Ordering[T]] // 获取一个Ordering[T]隐式值
ordering.compare(p1,p2)
}
}
object Person {
// 声明ordering[Person]的隐式值【对象】
implicit val ordering: Ordering[Person] = new Ordering[Person] {
override def compare(x: Person, y: Person): Int = if (x.age > y.age) 1 else -1
}
def main(args: Array[String]): Unit = {
val p1 = new Person("zs", 20)
val p2 = new Person("ls", 30)
val tools = new CompareUtils[Person](p1, p2)
if(tools.compare == 1) println("大于")
else println("小于")
}
}
集合
Scala提供了一套很好的集合实现,提供了一些集合类型的抽象。
Scala 集合分为可变的和不可变的集合:
- 可变集合(
mutable
) 可以在适当的地方被更新或扩展。这意味着你可以修改,添加,移除一个集合的元素。 - 不可变集合(默认
immutable
)类永远不会改变。不过,你仍然可以模拟添加,移除或更新操作。但是这些操作将在每一种情况下都返回一个新的集合,同时使原来的集合不发生改变。
顺带提一下java中的集合。。。
Java中集合结构:
|- Iterable
|- Collection
|- List 有序 有小标 元素可重复
|- ArrayList
|- Vector
|- LinkedList
|- Set 无序 无下标 元素不可重复
|- HashSet
|- TreeSet
|- LinkedHashSet
|- Map K-V键值对
|- HashMap
|- HashTable
|- TreeMap
|- Properties
seq序列
Range
数值范围区间
object Range {
def main(args: Array[String]): Unit = {
println(1 to 10) // => new Range(1,2,3,4,5,6,7,8,9,10)
println(new Range(1,10,2)) //(启始值,结束值,步长) => new Range(1,3,5,7,9)
}
}
//-----------------结果--------------------------------------
Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
Range(1, 3, 5, 7, 9)
Process finished with exit code 0
Vector(向量集合)
树形结构实现,查找效率高
println(Vector[Int](1,2,3,4,5,6))
println(Vector(1,2,3,4,5,6)) // =>自动类型推断
println(Vector("aa","bb","cc","dd"))
/************结果************************
Vector(1, 2, 3, 4, 5, 6)
Vector(1, 2, 3, 4, 5, 6)
Vector(aa, bb, cc, dd)
List (不可变的列表)
immutable
不可变的列表
// 声明
val list = List[Int](1,2,3,4,5,6,7)
println(list)
println(list(2)) //通果下标获取元素
//list(2) = 10 不可以对list集合中的元素进行更改
//list集合是不能更改的,要想添加一个元素,会返回一个新的list集合,
//原list集合元素不变
var list2 = list :+ 8 //添加元素
println(list2)
// 将0添加到集合的头部
val list3 = 0 +: list
println(list3)
// 移除元素
val list5 = list.drop(1)
println(list5)
Nil
空列表,没有任何元素的list集合
val list = Nil
::&:::
::
如:A :: List
将A作为整体添加到List集合的头部
::: 如:
A ::: List` 将A中的各个元素添加到List集合的头部
//感觉这些基础的语法在黑窗口里面敲比较尊重它
scala> 1 :: Nil
res20: List[Int] = List(1)
scala> 2:: res20
res21: List[Int] = List(2, 1)
scala> 3::res21
res22: List[Int] = List(3, 2, 1)
scala> List(1,2,3) :: res22
res23: List[Any] = List(List(1, 2, 3), 3, 2, 1)
scala> List(1,2,3) ::: res22
res24: List[Int] = List(1, 2, 3, 3, 2, 1)
ListBuffer(可变列表)
mutable 可以在原始集合的基础之上,进行改操作
// 声明一
scala> scala.collection.mutable.ListBuffer(1,2,3,4,5)
res29: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3, 4, 5)
// 声明二
scala> import scala.collection.mutable._ // 等价于 import scala.collection.mutable.*
scala> ListBuffer("A","B")
res30: scala.collection.mutable.ListBuffer[String] = ListBuffer(A, B)
// 使用
// 获取
scala> res1(1)
res2: String = B
// 修改
scala> res1(1) = "C"
scala> res1
res4: scala.collection.mutable.ListBuffer[String] = ListBuffer(A, C)
// 相同的对象
scala> res4 == res1
res5: Boolean = true
// 添加
scala> res1 += "D"
res6: res1.type = ListBuffer(A, C, D)
scala> res1.+=("E")
res7: res1.type = ListBuffer(A, C, D, E)
// 移除
scala> res1 -= "A"
res9: res1.type = ListBuffer(C, D, E)
// ++= 将后一个集合中的元素追加到当前集合的末尾
scala> res1 ++= ListBuffer("X","Y","Z")
res13: res1.type = ListBuffer(D, E, C, X, Y, Z)
// A +=:ListBuffer 表示将A添加到ListBuffer头部
scala> "o" +=: res1
res14: res1.type = ListBuffer(o, D, E, C, X, Y, Z)
结论
- 通常情况下不可变集合的操作符是不含
=
,操作完成后返回一个新集合- 通常情况下可变集合的操作符是含有
=
,操作时在当前集合完成的,不会返回新集合
遍历
没啥说的,上代码
scala> res1.foreach( println(_)) // “_“ 代表的是集合中的每一个元素
o
D
E
C
X
Y
Z
scala> for(n <- res1){println(n)}
o
D
E
C
X
Y
Z
scala> for(n <- 0 until res1.size){println(res1(n))}
o
D
E
C
X
Y
Z
set
set不可变(immutable)
// 声明
scala> scala.collection.immutable.Set[Int](1,2,3,2)
res24: scala.collection.immutable.Set[Int] = Set(1, 2, 3)
// 获取
scala> res24.foreach(println)
1
2
3
// 改操作
scala> res24 + 4
res26: scala.collection.immutable.Set[Int] = Set(1, 2, 3, 4)
scala> res24 + 3
res27: scala.collection.immutable.Set[Int] = Set(1, 2, 3)
scala> res24 == res26
res28: Boolean = false
scala> res24 - 2
res29: scala.collection.immutable.Set[Int] = Set(1, 3)
Set可变集合(mutable)
scala> scala.collection.mutable.Set[Int](1,2,3,3)
res31: scala.collection.mutable.Set[Int] = Set(1, 2, 3)
// 操作
scala> res31 += 4
res32: res31.type = Set(1, 2, 3, 4)
scala> res32 == res31
res33: Boolean = true
scala> res31 -= 2
res34: res31.type = Set(1, 3, 4)
// 遍历方法(略)
HastSet(略)
TreeSet(略)
这俩基本用不上,,,
Map
元素 k,v键值对
可变和不可变
// 方法一
// 可变
scala> Map("bj"->"北京","tj"->"天津")
res35: scala.collection.mutable.Map[String,String] = Map(tj -> 天津, bj -> 北京)
// 不可变
scala> scala.collection.immutable.Map[String,String]("bj"->"北京","tj"->"天津")
res36: scala.collection.immutable.Map[String,String] = Map(bj -> 北京, tj -> 天津)
// 方法二
scala> Map(("bj","北京"),("tj","天津"))
res37: scala.collection.mutable.Map[String,String] = Map(tj -> 天津, bj -> 北京)
scala.collection.immutable.Map(("bj","北京"),("tj","天津"))
// 操作
// 根据k取V值
scala> res0("bj")
res1: String = 北京
// 根据k修改v(可变集合)
scala> res3("bj")="bjj"
scala> res3
res5: scala.collection.mutable.Map[String,String] = Map(tj -> 天津, bj -> bjj)
//添加k v对
scala> res3.+=(("sh","上海"))
res6: res3.type = Map(tj -> 天津, bj -> bjj, sh -> 上海)
//根据k移除kv
scala> res3.remove("tj")
res7: Option[String] = Some(天津)
scala> res3
res8: scala.collection.mutable.Map[String,String] = Map(bj -> bjj, sh -> 上海)
// 通过声明Tuple1元组 删除kv
scala> res3 -= (("sh"))
res9: res3.type = Map(bj -> bjj)
scala> res3
res10: scala.collection.mutable.Map[String,String] = Map(bj -> bjj)
// 遍历
scala> res3.foreach(t => println(t._1 +"\t"+t._2))
bj bjj
// 键遍历
scala> res13.foreach(k =>println(res3(k)))
bjj
// 值遍历
scala> res3.values
res15: Iterable[String] = HashMap(bjj)
Tuple 元组
一种特殊的数据类型,可以存放任意的值,类似于
ArrayList[Object]
scala中内置了22个元组
Tupe1~Tuple22
通常使用Tuple2,描述Map集合的KV键值对
// 声明
scala> ("zs",1)
res18: (String, Int) = (zs,1)
scala> res18.getClass
res19: Class[_ <: (String, Int)] = class scala.Tuple2
scala> ("zs",1,false)
res20: (String, Int, Boolean) = (zs,1,false)
scala> res20.getClass
res21: Class[_ <: (String, Int, Boolean)] = class scala.Tuple3
// 获取元组中的值 语法: 元组引用名._n 获取元组中第n个值
scala> res20._1
res22: String = zs
scala> res20._2
res23: Int = 1
// 元组的遍历
scala> res20.productIterator
res24: Iterator[Any] = non-empty iterator
scala> for(n <- res24) println(n)
zs
1
false
Java和Scala集合的互操作
object JavaAndScalaCollectionOpt {
def main(args: Array[String]): Unit = {
// 导入隐式转换
import scala.collection.JavaConverters._
// 1. 创建Java集合
val list = new java.util.ArrayList[Int]()
list.add(1)
list.add(2)
list.add(3)
// 2. java集合转化scala的集合
val scalaList = list.asScala
val newScalaList = scalaList :+ 4
newScalaList.foreach(println)
// 3. scala集合转换java集合
val converterJavaList = newScalaList.asJava
println(converterJavaList.get(3))
}
}
模式匹配和样例类
match pattern
模式匹配是Scala中非常有特色,非常强大的一种功能。模式匹配,其实类似于Java中的swich case语法,即对一个值进行条件判断,然后针对不同的条件,进行不同的处理。
但是Scala的模式匹配的功能比Java的swich case语法的功能要强大地多,Java的swich case语法只能对值进行匹配。但是Scala的模式匹配除了可以对值进行匹配之外,还可以对类型进行匹配、对Array和List的元素情况进行匹配、对case class进行匹配、甚至对有值或没值(Option)进行匹配。
值匹配
/**
* 值匹配
*/
object MatchPattern01 {
def main(args: Array[String]): Unit = {
val str:String = "C"
// 传入的值和case的值进行精确匹配,和Java switch case同理,不存在穿透问题
str match{
case "A" => println("A")
case "B" => println("B")
case _ => println("其它") // `_` 表示default 其它
}
}
}
if守卫
不是for循环里面的if守卫啊,虽然区别不大
/**
* 值匹配
*/
object MatchPattern02 {
def main(args: Array[String]): Unit = {
val age:Int = 45 // 类似于: if... else if... else
// 传入的值和case的值进行精确匹配,和Java switch case同理,不存在穿透问题
// if守卫
age match{
case _ if(age > 60) => println("老年人") // `_` 匹配任意值
case _ if(age > 40) => println("中年人")
case _ if(age >= 20) => println("青年人")
case _ => println("少年&儿童") // `_` 表示default 其它
}
}
}
模式匹配中的变量
package matchpattern
/**
* 变量
*/
object MatchPattern03 {
def main(args: Array[String]): Unit = {
val age: Int = 18 // 类似于: if... else if... else
// 传入的值和case的值进行精确匹配,和Java switch case同理,不存在穿透问题
// if守卫
age match {
case a1 if (a1 > 60) => println("老年人:" + a1) // `_` 匹配任意值
case a2 if (a2 > 40) => println("中年人:" + a2)
case a3 if (a3 >= 20) => println("青年人:" + a3)
case _default => println("少年&儿童:" + _default) // `_` 表示default 其它
}
}
}
类型匹配
对表达式的类型进行匹配,Scala推荐使用这样的方式进行类型匹配,而不是使用
isInstanceOf
操作
package matchpattern
/**
* 类型匹配
*/
object MatchPattern04 {
def main(args: Array[String]): Unit = {
val t: Any = "aa"
// 传入的值和case的值进行精确匹配,和Java switch case同理,不存在穿透问题
// if守卫
t match {
case d: Double => println("小数:" + d)
case n: Int => println("整数:" + n)
case b: Boolean => println("布尔:" + b)
case _o => println("其它类型:" + _o)
}
}
}
集合元素匹配
个数,类型,类容
package matchpattern
/**
* 集合匹配
*/
object MatchPattern05 {
def main(args: Array[String]): Unit = {
val list = List[Int](1,2,3)
// scala编译器 编译完成后会抹掉集合类型的泛型信息,这样的话在运行时是无泛型的一种匹配,谁在最前面谁优先被执行
list match {
case l1:List[String] => println("list[String]")
case l2:List[Int] => println("list[Int]")
case _ => println("other")
}
}
}
al list = new java.util.ArrayList[Int]()
list.add(1)
list.add(2)
list.add(3)
// 2. java集合转化scala的集合
val scalaList = list.asScala
val newScalaList = scalaList :+ 4
newScalaList.foreach(println)
// 3. scala集合转换java集合
val converterJavaList = newScalaList.asJava
println(converterJavaList.get(3))
}
}
模式匹配和样例类
match pattern
模式匹配是Scala中非常有特色,非常强大的一种功能。模式匹配,其实类似于Java中的swich case语法,即对一个值进行条件判断,然后针对不同的条件,进行不同的处理。
但是Scala的模式匹配的功能比Java的swich case语法的功能要强大地多,Java的swich case语法只能对值进行匹配。但是Scala的模式匹配除了可以对值进行匹配之外,还可以对类型进行匹配、对Array和List的元素情况进行匹配、对case class进行匹配、甚至对有值或没值(Option)进行匹配。
值匹配
/**
* 值匹配
*/
object MatchPattern01 {
def main(args: Array[String]): Unit = {
val str:String = "C"
// 传入的值和case的值进行精确匹配,和Java switch case同理,不存在穿透问题
str match{
case "A" => println("A")
case "B" => println("B")
case _ => println("其它") // `_` 表示default 其它
}
}
}
if守卫
不是for循环里面的if守卫啊,虽然区别不大
/**
* 值匹配
*/
object MatchPattern02 {
def main(args: Array[String]): Unit = {
val age:Int = 45 // 类似于: if... else if... else
// 传入的值和case的值进行精确匹配,和Java switch case同理,不存在穿透问题
// if守卫
age match{
case _ if(age > 60) => println("老年人") // `_` 匹配任意值
case _ if(age > 40) => println("中年人")
case _ if(age >= 20) => println("青年人")
case _ => println("少年&儿童") // `_` 表示default 其它
}
}
}
模式匹配中的变量
package matchpattern
/**
* 变量
*/
object MatchPattern03 {
def main(args: Array[String]): Unit = {
val age: Int = 18 // 类似于: if... else if... else
// 传入的值和case的值进行精确匹配,和Java switch case同理,不存在穿透问题
// if守卫
age match {
case a1 if (a1 > 60) => println("老年人:" + a1) // `_` 匹配任意值
case a2 if (a2 > 40) => println("中年人:" + a2)
case a3 if (a3 >= 20) => println("青年人:" + a3)
case _default => println("少年&儿童:" + _default) // `_` 表示default 其它
}
}
}
类型匹配
对表达式的类型进行匹配,Scala推荐使用这样的方式进行类型匹配,而不是使用
isInstanceOf
操作
package matchpattern
/**
* 类型匹配
*/
object MatchPattern04 {
def main(args: Array[String]): Unit = {
val t: Any = "aa"
// 传入的值和case的值进行精确匹配,和Java switch case同理,不存在穿透问题
// if守卫
t match {
case d: Double => println("小数:" + d)
case n: Int => println("整数:" + n)
case b: Boolean => println("布尔:" + b)
case _o => println("其它类型:" + _o)
}
}
}
集合元素匹配
个数,类型,类容
package matchpattern
/**
* 集合匹配
*/
object MatchPattern05 {
def main(args: Array[String]): Unit = {
val list = List[Int](1,2,3)
// scala编译器 编译完成后会抹掉集合类型的泛型信息,这样的话在运行时是无泛型的一种匹配,谁在最前面谁优先被执行
list match {
case l1:List[String] => println("list[String]")
case l2:List[Int] => println("list[Int]")
case _ => println("other")
}
}
}