一、Scala的简介
1.1 scala的简介
1. Martin Odersky在2001年开始设计的,Java平台的Scala于2003年底/2004年初发布。
2. Scala名字由来:Scalable Language两个单词相结合;意大利语中 scala意为“梯子”或“楼梯”,蕴含“更佳的编程语言”。
3. 设计目标是将面向对象、函数式编程和强大的类型系统结合起来,让人要能写出优雅、简洁的代码。
1.2 scala的特点
1. 具有面向对象的特点
2. 具有函数式编程的特点
3. 具有静态类型的特点,也就是在编译期检查语法
4. 扩展性比较好
5. 并发性,scala使用的是actor的并发模型
1.3 函数式编程的简介
1. 函数式编程也是一种编程范式(范式就是编程思想)
2. scala函数是一等公民:指的是函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值
3. scala以表达式为中心
4. scala函数无副作用:只用纯函数来构造程序,或者说函数没有副作用。
一个函数在程序执行过程中除了根据输入参数给出运算结果外,没有其他的影响,就称为没有副作用的。
5. 引用透明:函数的运行不依赖于外部变量或"状态",只依赖于输入的参数,任何时候只要参数相同,引用函数所得到的返回值总是相同的。
6. Lambda表达式、闭包、高阶函数、柯里化
7. 递归与尾递归
总之,函数式编程的优点,代码简洁,开发快速;接近自然语言,易于理解;更方便的代码管理;易于"并发编程"
1.4 语言的类型
1.4.1静态语言和动态语言
静态编译语言:事先声明变量类型,类型不能改变,编译时检查;
动态编译语言:不用事先声明类型,随时可以赋值为其他类型,编程时不知道什么类型,很难推断;
静态语言和动态语言关键区别是何时获得类型信息,是在编译时间还是运行时间。
1.4.2强类型和弱类型
强类型语言:不同类型之间操作,必须强制类型转换为同一类型,print('a'+1):
弱类型语言:不同类型间可以操作,自动隐式转换。
强类型和弱类型语言的区别关键是类型之间的区别是否严格,例如语言是否会做字符串类型到数字类型的隐式转换。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8mMNPECG-1615727324335)(ClassNotes.assets/%E5%9B%BE%E7%89%872.jpg)]
二、Scala的安装
2.1 windows平台的安装
1. 去官网下载scala-2.11.8.msi(安装版本,需要点击下一步安装)或者scala-2.11.8.zip(解压版本,解压后就可以使用)
2. 安装到你喜欢的目录下,比如C:\Program File(x86)\
3. 配置环境变量(可以在用户变量下,也可以在系统变量下)
变量名:SCALA_HOME
变量值:C:\Program File(x86)\scala
变量名:PATH
变量值: 以追加的方式,添加一个scala的bin目录,比如C:\Program File(x86)\scala
4. 打开命令提示符界面,输入scala -version 检查版本号
5. 也可以输入scala,直接回车,进入命令行交互界面(REPL)
2.2 idea与scala的整合
2.2.1 离线安装(网络好的情况下,也可以在线安装)
1. 去idea的插件官网上下载相应版本的scala插件,注意版本号的问题,应该看一下idea版本与哪一款scala插件兼容,下载兼容的那一款。
官网:https://plugins.jetbrains.com/plugin/1347-scala
2. 将下载后的插件放入某一个目录下,比如idea软件自己的plugins目录下,创建一个scala目录来存储
如:D:\Users\Michael\Documents\IntelliJ IDEA 2019.3.1\plugins\scala\scala-intellij-bin-2019.3.23.zip
3. 打开idea, file-->settings-->plugins-->找到车轮图标里的install plugin from disk。
然后选中步骤2里的插件,然后重启idea
创建一个maven项目
1. 创建项目
2. 添加scala框架支持,选择windows上安装的scala安装路径
3. 在main目录下新建一个文件夹scala, 然后mark directory as Sources Root
4. 在scala目录下创建一个scala class. 注意:选择object
5. 打印helloworld
package com.qf.scala.day01
object Demo01 {
def main(args: Array[String]): Unit = {
print("hello world")
}
}
2.3 linux平台的安装
1. 在scala官网上下载scala-2.11.8.tgz
2. 上传,解压,[更名],配置环境变量
[root@qianfeng01 ~]# tar -zxvf scala-2.11.8.tgz -C /usr/local/
[root@qianfeng01 ~]# cd /usr/local/
[root@qianfeng01 local]# vim /etc/profile
..........省略..........
# scala environment
export SCALA_HOME=/usr/local/scala-2.11.8
export PATH=$SCALA_HOME/bin:$PATH
3. 测试版本号:
[root@qianfeng01 local]# scala -version
Scala code runner version 2.11.8 -- Copyright 2002-2016, LAMP/EPFL
三、Scala基础语法
3.1 scala解释器
1. scala解释器,指的就是命令行交互界面,也称之为REPL
(Read(取值)-> Evaluation(求值)-> Print(打印)-> Loop(循环)。)
案例演示1:
scala> 1+1
res0: Int = 2
解析: 输入的1+1 我们称之为表达式, 会输出结果res0: Int = 2
res0: 是scala解释器提供的内置变量,也可以继续使用它运算,比如
scala>res0*2.0
res1: Double = 4.0
注意:如果没有指定变量的类型,scala会自动推断变量的类型。
案例演示2:编译并执行scala程序
1. 准备工作,在D盘下创建一个scala目录,然后创建一个a.scala文件,编写一个输出hello world的经典小程序
object HelloWorld{
def main(args: Array[String]): Unit={
println("HelloWorld")
}
}
2. 在cmd中切换到D:/scala目录下
3. 编译scala源文件
D:\scala>scalac a.scala 注意:编译时要写源文件的具体名字。
4. 运行字节码文件
D:\scala>scala HelloWorld 注意:运行时要写字节码文件的具体名字
3.2 编程规范
1. scala源文件的后缀名必须是.scala
2. 源文件的名字最好和文件内部的最上面的类型名一致。
3. 源文件的字符集最好是UTF-8
4. 使用驼峰命名法,尽量见名知意(望文知意)
5. 使用空格代替tab键,最好是4个空格
6. 单行建议不超过150个字符,不超出可视范围
7. 花括号的用法:
- 左花括号({)前不能换行,在其后换行。
- 在右花括号(})前要有换行。
- 如果右花括号是一句语句、一个方法、构造函数或非匿名类的结尾,其后需要换行。
8. scala中的简单表达式可以省略括号
9. 空行的使用: 主要目的就是做逻辑分组
成员变量 和构造方法,和普通方法 之间使用空行隔开,显的比较优雅,好看
10. 注释:
单行注释: //
多行注释: /* */
文档注释: /** */
3.3 scala类型的讲解
3.3.1 类型的层次结构(重点)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b1KSJGQg-1615727324336)(ClassNotes.assets/20190610170115491.png)]
3.3.2 变量的使用
1. 变量的命名规则与java的一致,数字不能开头
2. 驼峰命名法,见名知意
3. 使用val或var声明变量,声明时必须初始化。
4. 在变量声明时,可以不指定类型,scala会自动推断变量的类型。也可以指定类型。
5. val声明的变量,相当于在变量前加了一个final,表示最终的值,就是不能再次修改值。
如果就是想要同一个变量,赋一个新值,就必须在添加val声明。
6. var声明的变量,可以再次被赋值。
7. 官方建议使用val。
- 更安全
- 代码可读性更高
- 资源回收更快,方法执行完,val所定义的变量即回收
3.3.3 整型类型
四个整型类型,分别是Byte,Short,Int,Long。相当于java的四个包装类,这些类型的值都可以调用自己的方法,比如:
val numStr:String = 10.toString() //将10转成了字符串类型
val r1 = 1.to(10) //获取一个Range对象
r: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
注意事项:
1. 在定义变量时,如果不明确指定类型,整型会默认推断成Int类型,不可以超范围的赋值,除非明确指定类型
2. 小于Int类型的数值做运算时,会自动隐式转换成Int类型,然后再做运算,结果是Int类型
3.3.4 浮点型
1. 有两个浮点型,分别是Float和Double。
2. 在给变量赋值时,如果没有明确指定类型,则是Double类型,如果想要赋值为Float,则需要添加F/f
3. 两个浮点型相加时,其结果通常情况下是不精确的。
4. 案例:
- 十进制数形式:如:5.12 512.0f .512 (必须有小数点);
- 科学计数法形式:如:5.12e2 = 5.12乘以10的2次方 5.12E-2 = 5.12除以10的2次方。
3.3.5 字符类型
1. 赋值方式有两种,分别是使用单引号或者是unicode对应的整数(0-65535)
val ch1 = 'A'
val ch2:Char = 65
2. 也可以使用16进制的方式,注意:8进制已经被弃用了
val ch3 = '\u0041' //显示的就是A
3.3.6 布尔类型
布尔类型只有两个值,分别是true和false.
val flag = true
val flag:Boolean = false
val flag:AnyVal = false
val flag:Any = true
3.3.7 字符串类型
1. 赋值特点必须使用双引号
2. 双引号中如果有特殊字符,需要使用转义符号\
比如,想要赋值一个变量为\"'。 那么写法如下
val str = "\\\"\'"
3. scala里引入了三层双引号,引号里是什么,就是什么
println("""welcome to
"1000phone".""")
4. scala的字符串类型其实就是java的String类型
"hello".getClass().getName()
"hello".getClass.getName
3.3.8 其他类型
Unit类型:
相当于java中的void类型。
赋值方式使用()
val a = ()
Any类型:
是所有类型的父类、超类、基类
AnyVal类型和AnyRef类型
是Any类型的两个子类型
Null类型:
是所有的AnyRef类型的子类型,只有一个实例就是null
Nothing类型
Nothing类型在Scala的类层级的最低端;它是任何其他类型的子类型。
当一个函数,我们确定没有正常的返回值,可以用Nothing 来指定返回类型,这样有一个好处,就是我们可以把返回的值(异常)赋给其它的函数或者变量(兼容性)
def a:Nothing = {
throw new Exception
}
Option类型:用于表示有值或者是无值。有值则是Some(xxxx),无值就是None。 Some和None是Option类型的两个实例。
scala> val myMap: Map[String, String] = Map("name" -> "zhangsan","age"->"23")
myMap: Map[String,String] = Map(name -> zhangsan, age -> 23)
scala> val value1: Option[String] = myMap.get("name")
value1: Option[String] = Some(zhangsan)
scala> val value1: Option[String] = myMap.get("address")
value1: Option[String] = None
3.3.9 类型转换
1. 自动类型转换:小范围类型转大范围类型,会自动转换。也叫隐式转换
val a:Long = 19 //Int的19 自动转成了Long类型的19
val b:Double = a //Long类型的a里的值转成了Double类型的b里的值。
2. 强制类型转换:大范围类型转小范围类型,需要强制转换,在scala中要调用方法
比如:
"19.9".toDouble
"19".toInt
"19".toDouble
def main(args: Array[String]): Unit = {
val a = 3 // 0000 0011
val b = 10 // 0000 1010
println(a&b) //0000 0010 // 全1为1
println(a|b) //0000 1011 // 有1为1
println(a^b) //0000 1001 // 相同为0,不同为1
}
3.4 scala操作符
1. 算术操作符
2. 关系操作符
3. 逻辑运算符
4. 位运算符
5. 赋值运算符
6. 运算符优先级
7. 注意事项: scala操作符都是方法。 比如 a+b 其实底层会转成a.+(b)
3.5 SCALA表达式(重点)
1. 什么是scala表达式
表达式就是一个语句块,包含一条或者多条语句
特点:
(1)表达式有返回值
(2)返回值是表达式最后一条语句的执行结果
2. 常见的scala表达式
- 块表达式
- if表达式
- 循环表达式
- 异常表达式
- Actor
3. 块表达式
- 即{}块,其内部包含了一系列的表达式。
- 块是有返回值的,块中最后的表达式值,就是块的值。
- 赋值语句本身返回unit类型,如果块的最后一个语句是赋值语句,则块的值返回()
- 块里的变量在块外不可见
3.6 分支结构
1. 共有三种写法,分别是单分支,双分支else,多分支else if。
2. 执行逻辑是某一个分支的条件表达式为true时才会执行,然后后续的分支直接跳过。
3. if分支是有返回值的。返回值的特点是多个块表达式返回值的最近的共同父类型。
3.7 循环结构
1. scala中有三种循环,分别是for循环,while循环,do-while循环
2. while循环和do-while循环和java里的语法基本一致。
3. for循环和java中的增强for循环比较相似。
- 语法:
for( 变量 <- Range|Array|List|其他表达式){
循环体
}
- 注意:for循环本身就是一个表达式,返回Unit
- 可以使用yield获取每一次循环体的返回值,封装到一个集合中。
- 高级for循环,其实就是java里的for循环的嵌套
for(范围表达式1;范围表达式2;[范围表达式3;[判断条件]]){
如果有条件、条件成立时才会执行循环体
}
4. scal循环的结束方式有三种,分别是
- return : 不仅会结束循环结构,还会结束整个方法。
- 循环条件:
- breakable: 需要导包。在里就可以使用break关键字。
(1)想要跳出整个循环结构,breakable块应该包含整个循环结构
(2)如果想要跳出当次循环,breakable块要包含循环体
3.8 插值器、正则表达式、文件IO
- 插值器的作用: 将变量的值插入到字符串中,使用变量的方式为${variable}
常用的三种插值器:s f raw
- 正则表达式: scala可以使用"正则表达式字符串".r方法获取正则表达式对象
findAllIn
findFirstIn
replaceAllIn
replaceFirstIn
......
- 文件IO: scala.io.Source
(1)Source.fromFile("文件路径")
(2)Source.fromURL("网络地址"),注意编码集的问题,要使用utf8
四、方法和函数
4.1 方法
- 如何定义方法
普通写法:def 方法名(参数类型列表):返回值类型={方法}
省略返回值写法:def 方法名(参数类型列表)={方法}
省略等号写法:def 方法名(参数类型列表){方法} --返回Unit
无参数列表写法:
def 方法名:返回值类型={方法} :调用时不能加()
def 方法名():返回值类型={方法} :调用时可加可不加
- 定义的位置
通常是在类体中,极少部分是在方法里定义
4.2 函数
1. 函数的定义
第一种:匿名内部类写法
val|var 函数名 = new FunctionN[参数列表]{
函数体
}
第二种:使用等号的写法
val f1 = ((a: Int, b: Int) => a + b) : val 函数名 =((参数列表)=>函数体)
val f2 = (a: Int, b: Int) => a + b : val 函数名 =(参数列表)=>函数体
val f3 = (_: Int) + (_: Int) : val 函数名 =(_:类型) 运算符 (_:类型)
第三种:使用冒号的写法
val f4:(Int, Int) => Int = (_ + _)
: val 函数名: 参数列表(指定形参类型=>返回值类型)= 函数体(_ 运算符 _)
val f5:((Int,Int)=>Int)={(x,y)=>x+y}
: val 函数名: 参数列表(指定形参类型=>返回值类型)={参数名称列表=>函数体}
val f6:(Int,Int)=>Int =(x,y)=>x+y
: val 函数名: 参数列表(指定形参类型=>返回值类型)= 参数名称列表=>函数体
第四种:特殊情况:无参数列表的写法
val f7=()=>{} :小括号不能省略
(1) 如果不带小括号,指显示实现关系
(2) 如果带小括号,就会执行函数体
2. 定义的函数底层是是实现了23个特质(与java中的接口相似),分别是Function0,Function1.....Function22
3. 定义位置:
因为函数是一等公民(对象),所以可以在任何地方定义。
4.3 方法和函数的转换
1. 函数可以作为方法的参数使用
2. 方法也可以作为函数的参数使用
3. 方法转函数: 使用神奇的下划线
方法名() _
五、集合框架
5.1 说明
集合框架的类型主要分布在两个包里,
- scala.collection.immutable 不可变集合 Scala默认采用不可变集合。
- scala.collection.mutable 可变集合
集合是用来存储一堆数据的。
集合框架中有常用的Array,List,Set,Map,tuple等等
5.2 Array
5.2.1 简介
1. Array也分两种类型,
一个是定长数组: 长度固定。可以增加,或移除元素,但是会返回一个新的数组对象
另一个是变长数组: 长度不固定,可以增删改元素,不会产生新的数组对象
2. 定长数组的定义:
- val 变量名 = new Array[T](Length)
- val 变量名 = Array(元素1[,.........])
3. 变长数组的定义:需要导包: scala.collection.mutable.ArrayBuffer
- val 变量名 = new ArrayBuffer[泛型](length) 但是length不生效
- val 变量名 = new ArrayBuffer[泛型]()
- val 变量名 = ArrayBuffer[泛型](元素1[,.....])
- val 变量名 = ArrayBuffer(元素1[,.....])
4. 数组的元素的访问
变量名(下标) :下标从0开始
5. 数组的元素类型应该相同
5.2.2 数组的转换
val a = Array(1,2,3,4,5)
val b = scala.collection.mutable.ArrayBuffer(2,2,3,4,5)
a.toBuffer: 变成了变长数组,返回的是新对象
b.toArray: 变成了定长数组,返回的是新对象
5.2.3 数组的遍历
package day03;
object _01ArrayDemo {
def main(args: Array[String]): Unit = {
val a = Array(1,2,3,4,5,6,7,8,9)
//遍历
for(elem <- a)
print(elem+" ")
println()
//使用下标遍历
for(i<- 0 until a.length)
print(a(i)+" ")
println
//使用foreach遍历
a.foreach(myprintln)
println()
a.foreach(x=>print(x+" "))
println()
a.foreach(print)
}
val myprintln:Int=>Unit =(x)=> print(x+" ")
}
5.2.4 数组的其他方法
参考代码中的 _01ArrayDemo _02ArrayDemo _03ArrayDemo
5.3 List列表
5.3.1 说明
1. list也分两类,
- 不可变list, 长度不可变,元素也不可变
- 可变list 长度可变,元素可变
2. 不可变list的构造方式有以下几种:
- val 变量名 = List(元素1[,.......])
- val 变量名 = List() 创建一个空List
- val 变量名 = Nil 创建一个空List
//也可以使用拼接符号:: 拼接,注意,这个符号只能将元素拼接到list的头部,尾部必须是一个list对象
- val 变量名 = "gaoyuanyuan"::Nil
- val 变量名 = "gaoyuanyuan"::List()
- val 变量名 = "gaoyuanyuan"::List("zhaoyouting")
//案例1:拼接一个集合的元素也是集合的集合
val list0 = List(1,1)::List(2,3)::Nil
val list1 = (7::8::Nil)::(9::10::Nil)::Nil
3. 可变List的构造方式:需要导入包
- val 变量名 = ListBuffer(元素1[,......])
- val 变量名 = ListBuffer()
- val 变量名 = ListBuffer[泛型]([元素1,......])
- val 变量名 = new ListBuffer[泛型]()
5.3.2 遍历
package day03
/**
* list的遍历
*
* 注意:不可变List的元素也不能修改
* 所以: names(2)="aaaa" 会报异常
*/
object _04ListDemo {
def main(args: Array[String]): Unit = {
val names = List("zhangsan","lisi","wangwu","liming")
//方式1:遍历list
names.foreach{x =>print(x+" ")}
println
//方法2:
names.foreach(x=>print(x+" "))
//方式3:
println(names.mkString(","))
//方式4:
for(i<- 0 until names.size)
print(names(i))
println()
//方法5:
println("--------------")
names.map(x=>print(x+" "))
}
}
5.3.3 常用的方法
head
tail
isEmpty
reverse
length
size
last
init
concat()
fill()
take()
takeWhile()
drop()
map()
foreach()
reduceLeft()
reduceRight()
foldLeft()()
foldRigth()()
splitAt(): 参数是长度
filter()
find()
partition()
span()
takeWhile()
dropWhile()
sortWith()
5.3.4 集合追加方式
1. :: 将元素添加到集合的头部, 此元素可以是一个普通元素,也可以是一个集合对象
.::(元素)
2.
+: 将紧挨着+的元素添加到冒号后面的集合中
:+ 将紧挨着+的元素添加到冒号前面的集合中
3.
++ 将两个集合连接起来,构成一个新集合 (可变不可变的集合,做连接,返回都是新集合对象)
++: 同上
::: 同上
4.
变量1.:::(变量2) : 将变量2插入到变量1 的前面
5.4
-- Set :无序,不重复
不可变Set: 有序,长度不变,元素不变,不重复
可变Set: 无序,不重复
-- Map: 存储一堆键值对的集合
不可变Map: 长度不变, 如果非要添加元素,则返回的是新的Map
可变Map: 指的是元素可变
-- Tuple: 元祖 可以存储不同类型的数据的一个集合,目前的长度只支持22个长度
定义方法:
val a = (1,2,3)
val b = new Tuple4("a","b",1,List(1,2,3))
访问元祖里的数据
_1 表示取第一个元素
-- 拉链方法:
zip: 长度可以不一样
zipAll: 长度不一样时需要添加两个默认值,
第一个默认值是调用方法的那个对象的值,
第二个默认值是括号里的那个对象的值
zipWithIndex:将变量的元素和索引做一个拉链
unzip: 解开拉链
六、Scala类的体系
6.1 class的定义
1. scala的class 默认都是public修饰的
2. class中的属性必须初始化
3. var声明的变量默认提供public修饰的get/set方法
val修饰的变量,提供getter方法,没有setter方法,也就是说是只读的,相当于java的final修饰的
4. 初始值可以使用占位符(_),但是必须指定类型,编译器会根据类型赋予类型的默认值
5. 初始值为null时,最好指定变量的类型,否则变量的类型为Null
6. private var声明的,有私有的getter方法和setter方法,只能在类的内部使用,其伴生对象也可以访问
private val声明的,有私有的getter方法,没有setter方法
/**
* 重要提示:自己手动创建变量的getter和setter方法需要遵循以下原则:
* 1. 成员变量必须私有化
* 2. 字段属性名以“_”作为前缀,如定义:_x
* 3. getter方法定义为:def x = _x
* 4. setter方法定义时,方法名为属性名去掉前缀,并加上后缀,后缀是:“x_ =”,如例子所示
*/
@BeanProperty注解可以放在变量的前面。会自动提供两个get方法,两个set方法
6.2 构造器的定义
与Java中一样,scala也可以有许多的构造器。但是,scala类的构造器分为主构造器(primary constructor)和辅助构造器(auxiliary constructor)
1. 主构造器的特点
- 主构造器的参数直接放置在类名之后。
- 一个类如果没有显式定义主构造器则自动拥有一个无参的主构造器
- 主构造器中的参数提供默认值,可以避免辅助构造器的过多定义
2. 辅助构造器的特点
- 辅助构造器的名称为this。(在java或c++中,构造器的名称和类名相同,修改类名时还要修改构造器名称)
- 每一个辅助构造器都必须在第一行调用调用一个先前已定义的其它辅助构造器或主构造器
- 辅助构造器的参数列表不能和主构造器的列表相同
6.3 抽象类的应用
1. 在Scala中,通过abstract关键字标记不能被实例化的类。
2. 方法不用标记abstract,只要省掉方法体即可。
3. 抽象类可以拥有抽象字段,(抽象字段/属性就是没有初始值的字段)
6.4 Trait的应用
/**
*1. Scala的trait 类似java里的接口,关键字是trait(特质)
*2. trait里的写法和scala的抽象类里可以提供的东西都是一样的
* 可以提供普通字段,抽象字段,普通方法,抽象方法
*3. trait的作用是用来被子类继承的,自己是不可以实例化
*4. 子类使用extends来继承一个trait
*5. 子类也可以继承多个trait,需要使用with关键字
*/
6.5 object,class的应用
1. 单例对象 :
- 同一个源文件中没有与之同名的class.
- 是一个特殊的class
- 直接调用名称,如果单例对象中有其他字段,方法,就可以名称.字段 名称.方法
2. 伴生对象
- 同一个源文件中有与之同名的class.
- object就是class的伴生对象,class就是object的伴生类
- 可以访问对方的私有成员
- 伴生对象中可以使用apply方法,可以简化伴生类的对象的创建 比如:val s1 = Student("zhangsan",23)
- 伴生对象中也可以使用apply方法来完成伴生类的单例对象
3. App
- 是一个Trait
- 继承了App的Object可以不提供main方法,就可以直接运行里面的逻辑
6.6 继承体系说明
1. 子类要使用extends继承父类,父类可以是一个普通类,也可以是一个抽象类,也可以是一个trait
2. 子类如果想实现多继承,可以使用with关键字
class A extends B with C with D
3. 子类继承了父类的所有的成员,但是父类中私有的成员,子类不可见
4. 子类继承的父类如果是一个trait或abstract,那么应该必须实现所有的抽象的成员,建议加上override。
5. trait也可以继承普通类
6. 子类可以使用super关键字调用父类的非私有成员
7. 可以使用isInstanceOf[ClassName]和asInstanceOf[className]进行判断和转换。
8. 父类型变量引用子类型对象:
val a:父类型名 = new 子类型构造器
闭包:指的是一个特殊的函数
1. 函数体里涉及到的所有变量中有函数体外部的变量。
比如下面的函数f4,函数体用到了外部的变量n,f4的返回值一定受外部变量n的影响, f4是闭包函数
val n = 10
def f4:(Int,Int)=>Int=(x,y)=>{
val m = 0;
x+y+m+n
}
柯里化函数
1. 多参数类型列表的函数就是柯里化函数
2. 底层原理:
先将第一个参数的值传入函数中,返回是带有第二个参数的一个函数,
然后再将第二个参数的值传入返回的函数中,进行运算。最终返回结果
泛型
java的泛型:
1. 方法的形参通常用于传入一个类的对象或值
sum(int x,Person p): 调用的时候传入int的一个值,Person类型的一个对象
2. 泛型是规定类或方法的参数要传入的类名
定义
class Person[T,E]{
private T name
private E age
public T sum(T m,E n){
return m
}
}
使用时:必须给泛型,也就是T赋值
Person p = new Person[String,int]()
String a = p.sum("aaa",23)
// +A 如果A是B的父类,那么List[A]就是List[B]的父类
// -A 如果A是B的父类,那么List[A]就是List[B]的子类
abstract class Animal {
def name: String
}
case class Cat(name: String) extends Animal
case class Dog(name: String) extends Animal
调用时,只能赋值泛型指定的类型的本类对象或者是子类对象,
Printer[+A]
定义时,如此规定:print(a:List[Cat])
使用时:只能传List[Cat],不能传List[Animal],List[Dog]
Printer[+A]
定义时,如此规定:print(a:List[Animal])
使用时:List[Cat],List[Animal],List[Dog]都可以传入
Printer[-A]
定义时,如此规定:print(a:List[Cat])
使用时:能传List[Cat],List[Animal],不可能List[Dog]
Printer[-A]
定义时,如此规定:print(a:List[Animal])
使用时:只能List[Animal],不能传List[Dog]和List[Cat],
上界和下界
B<:A 上界
A:>B 下界
作用:就是规定给泛型参数传值时的可使用范围。
n m
}
}
使用时:必须给泛型,也就是T赋值
Person p = new PersonString,int
String a = p.sum(“aaa”,23)
// +A 如果A是B的父类,那么List[A]就是List[B]的父类
// -A 如果A是B的父类,那么List[A]就是List[B]的子类
abstract class Animal {
def name: String
}
case class Cat(name: String) extends Animal
case class Dog(name: String) extends Animal
调用时,只能赋值泛型指定的类型的本类对象或者是子类对象,
Printer[+A]
定义时,如此规定:print(a:List[Cat])
使用时:只能传List[Cat],不能传List[Animal],List[Dog]
Printer[+A]
定义时,如此规定:print(a:List[Animal])
使用时:List[Cat],List[Animal],List[Dog]都可以传入
Printer[-A]
定义时,如此规定:print(a:List[Cat])
使用时:能传List[Cat],List[Animal],不可能List[Dog]
Printer[-A]
定义时,如此规定:print(a:List[Animal])
使用时:只能List[Animal],不能传List[Dog]和List[Cat],
上界和下界
B<:A 上界
A:>B 下界
作用:就是规定给泛型参数传值时的可使用范围。