面向对象编程,函数式编程
])的编程语言马丁·奥德斯基
(Martin Odersky)是编译器及编程的狂热爱好者,长时间的编程之后,希望发明一 种语言,能够让写程序这样的基础工作变得高效,简单。所以当接触到 JAVA 语言后,对 JAVA 这门便携式,运行在网络,且存在垃圾回收的语言产生了极大的兴趣,所以决定将函数式编程语言的特点融合到 JAVA 中,由此发明 了两种语言(Pizza & Scala)泛型,增强for 循环, 自动类型转换
等,都是从 Pizza 引入的新特性。类型推断,Lambda 表达式
就是从 scala 引入的特性。Scala 是一门以 java 虚拟机(JVM)为运行环境并将面向对象和函数式编程
的最佳特性结合在一起 的静态类型编程语言。
方法1: file->settings->pulgins ->搜索Scala->install
方法2: scala 插件点击此链接去官网下载插件
或者是file->settings->pulgins ->搜索Scala->Plugin homepage->下载idea对应的版本
然后file->settings->pulgins ->搜索Scala->设置->install Plugin from Disk
,选择你安装的插件->OK->重启idea
1) main->(右击)New->Directory->目录名(如scala)
2) scala->Mark Directory as->Sources Root
3) 右击项目名->Add Framework Support->勾选Scala->OK
(在默认情况下,idea 不支持 scala,需要引入 scala 的相关的开发包.所以需要此步.如果第一次选择,没有看到 scala 的包,请点击 configure ,选择 scala 的主目录即可。)
此时就可以在scala下创建包或Scala Class了
//1. object 是一个关键字,表示一个伴生对象
//2. 如果该文件只出现了一个 object HelloScala 就会在编译后产生两个.class 文件
//3. 第 1 个文件是 HelloScala.class 这个表示他的伴生类,但是空的.
//4. 第 2 个文件是 HelloScala$.class 对应的是 object HelloScala,但是本质是调用它对应的一个静态属性
object HelloScala {
// 1. def 表示一个方法或者一个函数
// 2. main 表示入口
// 3. args: Array[String] 表示形参,args 是形参名,Array[String]是形参类型,表示一个String类型的Array 数组
// 4. :Unit 表示返回值类型为 Unit ,等价于 java 的 void
// 5. = 表示 后面是函数体/方法体, 它还有返回值类型推导的作用
def main(args: Array[String]):Unit = {
// 表示的是输出,类似System.out.println("hello, scala")
// 在 scala 语句后不需要带像Java一样带;体现简洁
println("hello, scala")
}
}
为什么一个object HelloScala会产生两个.class文件?
因为在Java中static不是面向对象的,希望在Scala中一切都是对象.所以把静态的内容和非静态的内容分开,
HelloScala类的非静态内容
(属性,方法)放在class HelloScala{ }
HelloScala类的静态内容
(属性,方法)放在object HelloScala{ }
def 函数名 ([参数名: 参数类型], …)[[: 返回值类型] =] { 语句… //完成某个功能 return 返回值 }
: 返回值类型 =
表示有返回值,并且指定了返回值的类型=
, 表示返回值类型,使用类型推导空的
,表示没有返回值,即使有 return 也不生效光标点击需要查看源码的地方(如类或方法等),然后按Ctrl+b
点击右上角的Attach Sources
使用Ctrl+Alt+L
来进行格式化
运算符两边尽量各加一个空格.比如1 + 2 * 3
//
单行注释
/*多行注释*/
/**文档注释*/
文档注释: 注释内容可以被工具 scaladoc 所解析,生成一套以网页文件形式体现该程序的说明文档
package com.hyj.chapter02
object DocumentCommen{
def main(args: Array[String]): Unit = { }
/**
* @param n1 传入一个整数 n1
* @param n2 传入一个整数 n2
* @return 返回一个整数值
*/
def sub(n1:Int,n2:Int): Int = {
return n1 - n2
}
}
变量相当于内存中一个数据存储空间的表示,通过变量名可以访问到变量(值).
scala 要求变量声明时必须要初始化.
var 变量名[:变量类型]=变量值
val 常量名[:常量类型]=常量值
声明变量时,类型可以省略,因为类型可以推断出来
能够使用val,就不要使用var,因为val是线程安全的,效率高
var 修饰的对象引用可以改变,val 修饰的则不可改变,但是对象的状态(值)却是可以改变的
_
开头,后接字母数字或下划线+ - * / # !等
object InputDemo {
def main(args: Array[String]): Unit = {
val name:String ="张三"
var age:Int =20
var money:Double =1820.5626
//字符串,通过+来连接
println(name+"今年"+age+"岁了") //张三今年20岁了
//printf用法,通过%来传值
printf("%s今年得了%.2f元压岁钱\n",name,money) //张三今年得了1820.56元压岁钱
//将字符串复制3次
println(name*3) //张三张三张三
//字符串模板(插值字符串):通过$获取变量值
println(s"name=${name} age=${age+1} money=${money}") //name=张三 age=21 money=1820.5626
//格式化模板字符串
println(f"${name}今年得了${money}%8.2f压岁钱") //张三今年得了 1820.56压岁钱
println(raw"${name}今年得了${money}%8.2f压岁钱") //张三今年得了1820.5626%8.2f压岁钱
//三引号表示字符串,保持多行字符串的原格式输出
var sql=
s"""
|select *
| from
|people
| where
|name=${name}
| and
| age<${age}
|""".stripMargin
println(sql)
}
}
import scala.io.StdIn
object InputDemo {
def main(args: Array[String]): Unit = {
print("请输入你的名字:>")
//接收用户从键盘输入的数据(字符串)
val name=StdIn.readLine()
print("请输入你的年龄:>")
val age=StdIn.readInt()
print("请输入你的工资:>")
val sal=StdIn.readDouble()
println("名字:"+name+" 年龄"+age+" 工资"+sal)
}
}
在Scala语言的Source单例对象中, 提供了一些非常便捷的方法, 从而使开发者可以快速的从指定数据源(文本文件, URL地址等)中获取数据, 在使用Source单例对象之前, 需要先导包, 即import scala.io.Source
迭代器类型的对象
. 然后通过toArray, toList
方法, 将这些数据放到数组或者列表中即可.package testpackage
import scala.io.{BufferedSource, Source}
object TestDemo2 {
def main(args: Array[String]): Unit = {
//1.获取数据源文件对象
val source: BufferedSource = Source.fromFile("src/main/resources/a.txt")
//2.以行为单位读取数据
val lines: Iterator[String] = source.getLines()
//3.将读取到的数据封装到列表中
val list:List[String] = lines.toList
println(list) //List(好好学习 天天向上!, hello Hadoop Zookeeper, hello Flume Spark Flink, hello Sqoop HBase)
//4.关闭Source对象
source.close()
}
}
以字符为单位读取数据
这种方式, 这种用法类似于迭代器, 读取数据之后, 我们可以通过hasNext(),next()
方法, 灵活的获取数据.package testpackage
import scala.io.{BufferedSource, Source}
object TestDemo2 {
def main(args: Array[String]): Unit = {
//1.获取数据源文件对象
val source: BufferedSource = Source.fromFile("src/main/resources/a.txt")
/* //2.以字符为单位读取数据
val iter: BufferedIterator[Char] = source.buffered
while (iter.hasNext){
print(iter.next())
} */
//如果文件不是很大,我们可以直接把它读取到一个字符串中
val str: String = source.mkString
println(str)
//4.关闭Source对象
source.close()
}
}
特定符号间隔开
的字符串, 如果数据源文件中的数据都是数字形式的字符串
, 我们可以很方便的从文件中直接获取这些数据, 例如:package testpackage
import scala.io.{BufferedSource, Source}
object TestDemo2 {
def main(args: Array[String]): Unit = {
//1.获取数据源文件对象
val source: BufferedSource = Source.fromFile("src/main/resources/b.txt")
//2.读取词法单元
// \s表示空白字符(空格,\t,\r,\n等) \s+表示1个或多个空白字符
val arr: Array[String] = source.mkString.split("\\s+")
//3.将字符串转换成对应的整数
val ints: Array[Int] = arr.map(_.toInt)
for (num <- ints) {
print(num + " ")
}
//4.关闭Source对象
source.close()
}
}
Scala中提供了一种方式, 可以让我们直接从指定的URL路径, 或者其他源(例如: 特定的字符串)中直接读取数据。
//1.获取数据源文件对象
val source: BufferedSource = Source.fromURL("https://www.csdn.net")
//将数据封装到字符串中并打印
println(source.mkString)
//3.关闭Source对象
source.close()
val source: Source = Source.fromString("hello scala\nhello kafka!!!")
val iter: Iterator[String] = source.getLines()
while (iter.hasNext){
println(iter.next())
}
Scala没有提供读取二进制文件的方法, 我们需要通过Java类库来实现.
package testpackage
import java.io.{File, FileInputStream}
object TestDemo2 {
def main(args: Array[String]): Unit = {
//创建File对象,关联数据源文件
val file: File = new File("src/main/resources/img.png")
//创建字节输入流,用来读取数据
val stream: FileInputStream = new FileInputStream(file)
//创建字节数组,用来存储读取到的数据(字节)
val bytes: Array[Byte] = new Array[Byte](file.length().toInt)
//开始读取,将读取到的数据存储到字节数组中,并返回读取到的有效字节数
val i: Int = stream.read(bytes)
println("读取到的有效字节数:"+i) //190294
println("字节数组的长度:"+bytes.length) //190294
//关闭字节输入流
stream.close()
}
}
package testpackage
import java.io.FileOutputStream
object TestDemo2 {
def main(args: Array[String]): Unit = {
//写入数据到文本文件
//创建字节输出流对象,关联目的地文件
val fileOutputStream: FileOutputStream = new FileOutputStream("./data/c.txt")
fileOutputStream.write("hello scala\r\n".getBytes)
fileOutputStream.write("hello flink\r\n".getBytes)
//关闭字节输出流
fileOutputStream.close()
}
}
object InputDemo {
def main(args: Array[String]): Unit = {
//从文件中读取数据
Source.fromFile("src/main/resources/testfile").foreach(print)
//将数据写入文件
val writer = new PrintWriter(new File("src/main/resources/output.txt1"))
writer.write("helloscala\n") //不具备换行功能
writer.write("spark")
writer.close()
}
}
Java的基本数据类型不是真正意义上的对象,即使后面产生了基本类型的包装类,但是仍然存在基本数据类型,所以Java语言并不是真正的面向对象.
Scala 与 Java 有着相同的数据类型,在 Scala 中数据类型都是对象,也就是说 scala 没有 java 中的原生类型
在Scala中一切皆是对象,都是Any的子类
Scala数据类型分为两大类AnyVal(数值类型,可以理解为Java的基本数据类型)
和AnyRef(引用类型)
1)Any是所有类的的根类型,即所有类的父类(基类)
2)Null
类型只有一个实例null
,它是AnyRef的子类
3)Nothing类型
是所有类的子类,它可以作为没有正常返回值
的方法的返回类型,比如:
object InputDemo {
def main(args: Array[String]): Unit = {
def test(n: Int): Any = {
if (n == 1)
return n
else
throw new NullPointerException
}
print(test(1))
//表示fun1方法就是没有正常的返回值,专门用于返回异常
def fun1():Nothing={
throw new Exception("异常发生")
}
}
}
4)Unit
类型只有一个实例()
Byte 1字节
,Short 2字节
,Int 4字节
,Long 8字节
Float 4字节
,Double 8字节
Char 16位无符号Unicode字符
,String 字符串
Boolean 1字节
Unit 表示无值,和void等同,用作不反回任何结果的方法的结果类型
1)当Scala程序在进行赋值或者运算时,精度小的类型自动转换为精度大的数据类型,这个就是自动类型转换(隐式转换)
2)有多种类型的数据混合运算时,系统首先自动将所有数据转换成容量最大的那种数据类型
,然后再进行计算.
3)(Byte,Short)和Char之间不会相互自动转换
4)Byte,Short,Char它们三者可以计算,在计算时首先转换为Int类型
5)自动提升原则:表达式结果的类型自动提升为操作数中最大的类型
6) 当我们把精度(容量)大 的数据类型赋值给精度(容量)小 的数据类型时,就会报错,反之就会进行自动类型 转换
object DataTypeDemo {
def main(args: Array[String]): Unit = {
val l:Long =10
//将a强制转换为Int类型
println(l.toInt) //10
val d:Double=1.2345678
//将d强制转换为Byte类型
println(d.toByte) //1
//格式化输出,保留小数点2位,并进行四舍五入
println("d="+d.formatted("%.2f")) //d=1.23
var num : Int =10 * 3.5.toInt + 6 * 1.5.toInt
var num2 :Int=(10 * 3.5 + 6 * 1.5).toInt //44.0
println(num) //36
println(num2) //44
val a:Int =12
val c:Char = a.toChar //必须强转,否则报错
val c2:Char =12
//var char1: Char = 97 + 1 //报错,因为运算就会有类型,Int=>Char
var char2: Char = 98 //没有运算,编译器只判断范围有没有越界
}
}
当对一个数取模时,可以等价 a%b=a-a/b*b
,和java 的取模规则 一样
注意:Scala 中没有++、–操作符,需要通过+=、-=来实现同样的效果
object InputDemo {
def main(args: Array[String]): Unit = {
println(10 % 3) //10 - 10/3 * 3 = 10 - 3 * 3 =1
println(-10 % 3) // -10 - -10/3 *3 = -10 + 3 * 3 =-1
println(10 % -3) // 10 - 10/-3 * -3 =10 - -3 * -3 =10 - 9=1
println(-10 % -3) // -10 - -10/-3 * -3 = -10 - 3 * -3 = -10 + 9 =-1
}
}
按位与&
,按位或|
,按位异或^
所有的运算都是以二进制补码进行的
补码的方式来运算
的,但是返回结果时,其实会将补码转成原码
object InputDemo {
def main(args: Array[String]): Unit = {
val b: Int = 129
// 129的原码:00000000 00000000 00000000 10000001
//反码: 00000000 00000000 00000000 10000001
// 补码: 00000000 00000000 00000000 10000001
// 正数的原码反码补码一样
// 截取最后一个字节,Byte 得到补码10000001
// 10000000 反码
// 11111111 原码
print(b.toByte) //-127
val a: Int = 19 //10011
val c: Int = 45 //101101
// 10011
// 101101
// 000001
print(a & c)//1
}
}
equals()比较的是值是否相等
eq()比较的是地址是否相等
==,如果比较的对象是null,调用的是eq()方法; 如果比较的对象不是null,调用的是equals()方法
object InputDemo {
def main(args: Array[String]): Unit = {
val str1:String="scala"
val str2:String=new String("scala")
println(str1==str2) //true
println(str1.equals(str2)) //true
println(str1.eq(str2)) //false
}
}
object TestDemo {
def main(args:Array[String]):Unit={
val str1:String=null
val str2:String="scala"
val str3:String=null
println(str1==str2) //false
println(str1==str3) //true
}
}
object InputDemo {
def main(args: Array[String]): Unit = {
val n1: Int = 25
val n2: Int = 2
//n1这个Int型的对象调用+方法,.点号可以省略,因为只有一个参数,所以()也可以省略
println(n1.+(n2))
println(n1+n2)
println(n1.*(n2))
println(n1*n2)
println(12.34.*(2))
println(7.5.toInt)
//.点号可以省略,用空格代替
println(7.5 toInt)
}
}
scala 中,没有三目,使用 if – else 替代
if–else是有返回值的,具体返回 结果的值取决于满足条件的代码体的最后一行内容
object TestDemo {
def main(args:Array[String]):Unit={
val x:Int= 30;
if( x == 10 ){
println("x的值为 10")
}else if( x == 20 ){
println("x的值为 20")
}else if( x == 30 ){
println("x的值为 30")
}else{
println("无法判断x的值")
}
}
}
object ifElseDemo {
def main(args: Array[String]): Unit = {
print("请输入你的年龄:>")
var age:Int =StdIn.readInt()
val str:String =if(age>18) "成年" else "未成年" //else if
println(age+"->"+str)
}
}
object forDemo {
def main(args: Array[String]): Unit = {
//i将会从1-10循环,前后闭合,(包括1和10)
for(i <- 1 to 10){
print(i+" ")
}
//上面的本质是1(对象)调用to()方法
for (i <- 1.to(10)) {
print(i+" ")
}
for (i <- 1 to 10 by 3) { //步长为3
print(i + " ") //1 4 7 10
}
for (i <- 1 to 10 reverse) {
print(i + " ") //10 9 8 7 6 5 4 3 2 1
}
println()
//利用伴生对象创建一个集合
val list:List[String] =List("北京","广州","上海","深圳","杭州")
//遍历集合
for(item <- list){
print(item+" ")
}
println()
//i将会从1-9循环,前闭后开 [1,10)
for(i <- 1 until 10){
print(i+" ")
}
println()
//循环守卫即循环保护式,保护式为true则进入循环体内部,为false则跳过,类似于contince
for(i <- 1 to 10 if i!=2){
print(i+" ")
}
println()
//上面的代码和下面等价
for(i <- 1 to 10){
if(i!=2){
print(i+" ")
}
}
println()
val list2:List[String] =List("bj","sh","hz","sz","wh")
//集合进行遍历,使用循环守卫
for(item <- list2 if item.startsWith("s")){
print(item+" ")
}
println()
//引入变量
for(i <- 1 to 10;j=4-i){
print(j+" ")
}
println()
//上面的代码等价于↓
for(i <- 1 to 10){
val j=4-i
print(j+" ")
}
println()
//嵌套循环
for(i <- 1 to 3;j <- 1 to 3){
println("i="+i+" j="+j)
}
//上面的代码等价于↓
for(i <- 1 to 3){
for(j <- 1 to 3){
println("i="+i+" j="+j)
}
}
/* {}和()对于for表达式来说都可以
for推导式有一个不成文的约定,当for推导式仅包含单一表达式使用圆括号,当其包含多个表达式时使用大括号,
当使用{}来换行写表达式时,分号就不用写了*/
//方式一:
for{i <- 1 to 3 ;j <- 1 to 3}{
println("i="+i+" j="+j)
}
//方式二:
for{i <- 1 to 3
j <- 1 to 3}{
println("i="+i+" j="+j)
}
//循环返回值:将遍历过程中处理的结果返回到一个新的Vector集合中,使用yield关键字,yield可以写代码块
val res=for(i <- 1 to 5) yield i*2
println(res) //Vector(2, 4, 6, 8, 10)
val res2=for(i <- 1 to 10) yield {
if(i%2==0){
i
}
}
println(res2) //Vector((), 2, (), 4, (), 6, (), 8, (), 10)
//遍历1-10(不包括10),步长为3,Range是一个集合
for(i <- Range(1,10,3)){
print(i+" ")
}
}
}
Scala内置控制结构去掉了break和contince.
import scala.util.control.Breaks.{break, breakable}
object breakDemo {
def main(args: Array[String]): Unit = {
//在for循环中仍然使用break()中断循环
breakable{ //表示接下来的代码是可中断的
for(i <- 1 to 10000){
if(i==20){
break()
}
print(i+" ")
}
}
}
}
可变参数一般放置在最后
object InputDemo {
def main(args: Array[String]): Unit = {
def fun1(str: String*): Unit = {
println(str)
}
fun1("aaa", "bbb") //WrappedArray(aaa, bbb) 此时返回一个集合
fun1() //List()
def fun2(str1: String, str2: String*): Unit = {
println("str1=" + str1 + " str2=" + str2)
}
//str1=aaa str2=WrappedArray(bbb, ccc, ddd)
fun2("aaa", "bbb", "ccc", "ddd")
//参数默认值
def fun3(str1: String = "hyj"): Unit = {
println(str1)
}
fun3() //hyj
fun3("huo") //huo
def fun4(name: String, age: Int): Unit = {
println("name=" + name + " age=" + age)
}
//带名传参
fun4(age = 20, name = "hyj") //name=hyj age=20
}
}
:
和返回值类型一起省略)=
def fun1(): Unit = {
println("hello")
}
fun1()
fun1
def fun2: Unit = {
println("world")
}
fun2
def fun3(name: String): Unit = {
println(name)
}
//匿名函数,lambda表达式
(name: String) => {
println(name)
}
object InputDemo {
def main(args: Array[String]): Unit = {
val fun = (name: String) => {
println(name)
}
fun("hello")
//定义一个函数,以函数作为参数输入
def f(func: String => Unit): Unit = {
func("scala")
}
f((name: String) => {
println(name)
})
}
}
没有名字的函数就是匿名函数.(x:Int)=>{函数体}
传递匿名函数至简原则:
_
代替object InputDemo {
def main(args: Array[String]): Unit = {
//定义一个函数,以函数作为参数输入
def f(func: String => Unit): Unit = {
func("scala")
}
f((name: String) => {
println(name)
})
//1) 参数的类型可以省略,会根据形参进行推导
f((name) => {
println(name)
})
//2) 类型省略之后,发现只有一个参数,则圆括号可以省略;其他情况:没有参数和参数超过1的永远不能省略圆括号
f(name => {
println(name)
})
//3) 匿名函数如果只有一行,则大括号也可以省略
f(name => println(name))
//4) 如果参数只出现一次,则参数省略且后面参数可以用`_`代替
f(println(_))
//5)如果可以推断出,当前传入的println是一个函数体,而不是调用语句,可以直接省略下划线
f(println)
}
}
object InputDemo {
def main(args: Array[String]): Unit = {
def f(fun: (Int, Int) => Int): Int = {
fun(1, 2)
}
println(f((a: Int, b: Int) => {a + b}))
println(f((a, b)=> a + b))
println(f( _ + _ ))
println(f(_ - _))
println(f(-_ + _))
}
}
object InputDemo {
def main(args: Array[String]): Unit = {
def fun(): Int = {
println("函数可以作为值进行传递")
666
}
//函数可以作为值进行传递
val a: Int = fun()
val b = fun
println(a + " " + b)
//在被调用函数fun后面加上_,相当于把函数fun当成一个整体传递给变量f
val f = fun _
f()
println(f) //函数引用
//注意:此时仅仅一个f是不可以的
//如果明确变量类型,那么不使用下划线也可以将函数作为整体传递给变量
val f1: () => Int = fun
f1()
println(f1) //函数引用
}
}
object InputDemo {
def main(args: Array[String]): Unit = {
def fun1(): Int => Unit = {
def fun2(a: Int): Unit = {
println("fun2被调用 " + a)
}
fun2 //将函数直接返回
}
fun1()(2)
}
}
object InputDemo {
def main(args: Array[String]): Unit = {
val arr: Array[Int] = Array(11, 22, 33, 44, 55, 66)
def arrayOperation(array: Array[Int], op: Int => Int): Array[Int] = {
for (elem <- array) yield op(elem)
}
def addTwo(elem: Int): Int = {
elem + 2
}
val newArray: Array[Int] = arrayOperation(arr, addTwo)
println(newArray.mkString(",")) //13,24,35,46,57,68
//传入匿名函数
val newArray2 = arrayOperation(arr, (elem: Int) => { elem * 2 })
val newArray3 = arrayOperation(arr, _ * 2 )
}
}
练习:定义一个函数fun,它接收一个Int类型的参数,返回一个函数(记作fun2).它返回的函数fun2,接收一个String类型的参数,同样返回一个函数(记作fun3).函数fun3接收一个Char类型的参数,返回一个Boolean的值.要求调用fun(0)(“”)(‘0’)得到返回值为false,否则为true.
object InputDemo {
def main(args: Array[String]): Unit = {
// def fun(i: Int): String => (Char => Boolean) = {
// def fun2(s: String): Char => Boolean = {
// def fun3(c: Char): Boolean = {
// if (i == 0 && s == "" && c == '0') false else true
// }
//
// fun3
// }
//
// fun2
// }
// println(fun(0)("")('0'))
//匿名函数(lambda表达式)简写
/* def fun(i: Int): String => (Char => Boolean) = {
s => c => if (i == 0 && s == "" && c == '0') false else true
}
println(fun(0)("")('0')) */
//柯里化
def fun(i: Int)(s: String)(c: Char): Boolean = {
if (i == 0 && s == "" && c == '0') false else true
}
println(fun(1)("hello")('0'))
}
}
闭包
:如果一个函数,访问到了它的外部(局部)变量的值,那么这个函数和它所处的环境,称为闭包.(调用函数时相当于创建了一个对象实例,把函数所依赖的环境和局部变量打包保存在这个对象实例里)函数柯里化
:柯里化(Currying)是指将原先接受多个参数的方法转换为多个只有一个参数的参数列表的过程。尾递归的原理:当编译器检测到一个函数调用是尾递归的时候,它就覆盖当前的活动记录而不是在栈中去创建一个新的。编译器可以做到这点,因为递归调用是当前活跃期内最后一条待执行的语句,于是当这个调用返回时栈帧中并没有其他事情可做,因此也就没有保存栈帧的必要了。通过覆盖当前的栈帧而不是在其之上重新添加一个,这样所使用的栈空间就大大缩减了,这使得实际的运行效率会变得更高。
object InputDemo {
def main(args: Array[String]): Unit = {
//递归实现计算阶乘
def fact(n: Int): Int = {
if (n == 0) return 0
fact(n - 1) * n
}
//尾递归实现
def tailFact(n: Int): Int = {
@tailrec
def loop(a: Int, currRes: Int): Int = {
if (a == 0) return currRes
loop(a - 1, currRes * a)
}
loop(n, 1)
}
println(tailFact(10))
}
}
控制抽象也是函数的一种, 它可以让我们更加灵活的使用函数.
假设函数A的参数列表需要接受一个函数B, 且函数B没有输入值也没有返回值
, 那么函数A就被称之为控制抽象函数
.
格式:
def 函数A (函数B: () => Unit) = {
//代码1
//代码2
//...
函数B()
}
object TestDemo2 {
def main(args: Array[String]): Unit = {
val myShop = (f1: () => Unit) => {
println("Welcome in!")
f1()
println("Thanks for coming!")
}
//2. 调用函数
myShop {
() =>{
println("我想买一个笔记版电脑")
println("我想买一个平板电脑")
println("我想买一个手机")
}
}
}
}
() => Unit
是一个函数(参数列表为空).
=> Unit
传名参数,参数是一个返回值为Unit的代码块.
代码块是有返回值的,其最后一行作为返回值.
object InputDemo {
def main(args: Array[String]): Unit = {
def fun(): Int = {
println("fun调用")
666
}
//传名参数,传递的不是具体的值,而是代码块
def fun2(a: => Int):Unit={
println("a: "+a)
println("a: "+a)
}
fun2(11)
println("=========================")
fun2(fun())
println("=========================")
fun2({
println("这是一个代码块")
888
})
}
}
a: 11
a: 11
=========================
fun调用
a: 666
fun调用
a: 666
=========================
这是一个代码块
a: 888
这是一个代码块
a: 888
object InputDemo {
def main(args: Array[String]): Unit = {
//用闭包实现一个While循环,将代码块作为参数传入,递归调用
def myWhile(condition: => Boolean): (=> Unit) => Unit = {
//内层函数需要递归调用,参数就是循环体
def doLoop(op: => Unit): Unit = {
if (condition) {
op
myWhile(condition)(op)
}
}
doLoop _
}
var n: Int = 10
myWhile({n >= 1})({
println(n)
n -= 1
})
// myWhile(n >= 1){
// println(n)
// n -= 1
// }
//2.用匿名函数实现
def myWhile2(condition: => Boolean): (=> Unit) => Unit = {
op => {
if (condition) {
op
myWhile2(condition)(op)
}
}
}
//3.用柯里化实现
def myWhile3(condition: => Boolean)(op: => Unit):Unit = {
if (condition) {
op
myWhile2(condition)(op)
}
}
}
}
当函数返回值被声明为lazy时,函数的执行将被推迟
,直到我们首次对此取值,该函数才会执行.这种函数我们称之为惰性函数.
object InputDemo {
def main(args: Array[String]): Unit = {
lazy val result = sum(66, 88)
println("======================")
println("result= " + result)
println("result= " + result)
def sum(n1: Int, n2: Int): Int = {
println("sum调用")
return n1 + n2
}
}
}
======================
sum调用
result= 154
result= 154
命名规则:只能包含数字,字母,下划线,小圆点,但是不能用数字开头,也不要使用关键字
com.公司名.项目名.业务模块名
说明:Scala有两种包的管理风格,一种方式和Java的包管理风格相同,每个源文件一个包(包名和源文件所在路径不要求必须一致).包名用.
进行分割以表示包的层级关系,比如com.hyj.scala
.另一种风格,通过嵌套的风格表示层级关系.
package com{
package hyj{
package scala{
}
}
}
第二种风格有如下特点:
//用嵌套风格定义包
package com{
import com.hyj.scala.Inner //需要导包
//在外层包中定义单例对象
object Outer{
var out:String="out"
def main(args: Array[String]): Unit = {
println(Inner.in)
}
}
package hyj{
package scala{
//在内层包中定义单例对象
object Inner{
val in:String="In"
def main(args: Array[String]): Unit = {
println(Outer.out) //out
Outer.out="outer"
println(Outer.out) //outer
}
}
}
}
}
//在同一文件中定义多个包
package aaa{
package bbb{
import com.hyj.scala.Inner
object Test_01{
def main(args: Array[String]): Unit = {
println(Inner.in)
}
}
}
}
包对象:在Scala中可以为每个包定义一个同名的包对象,定义在包对象中的成员,作为其对应包下所有class和object的共享变量,可以直接访问.
package ccc {
package ddd {
object Test_02 {
def main(args: Array[String]): Unit = {
println(name)
println(sum(10,20))
println(school)
}
}
}
// 定义一个包对象(必须和ddd包在同一层级上)
package object ddd {
val school: String = "bili"
}
}
// 定义一个包对象(必须和ccc包在同一层级上)
package object ccc {
val name: String = "scala"
def sum(a1: Int, a2: Int): Int = {
a1 + a2
}
}
定义(ava的包管理风格):
右击new Package Object
new一个包对象
package object com{
val shareValue="share"
def shareMethod(): Unit ={
println(s"我们在${shareValue}")
}
}
说明:若使用Java的包管理风格,则包对象一般定义在其对应包下的package.scala文件中,包对象与包名保持一致.
屏蔽类:import java.util.{ArrayList=>_,_}
new _root_.java.util.HashMap
Scala中的三个默认导入分别是:
import java.lang._
import scala._
import scala.Predef._
回顾Java中的类:
注意:Scala中没有public,一个.scala中可以写多个类.
Scala语法中,类并不声明为public,所有这些类都具有公有可见性(即默认就是public)
当将scala字段注解为@BeanProperty时,会自动生成get/set方法
object InputDemo {
def main(args: Array[String]): Unit = {
val student = new Student()
student.setName("tom")
student.age=20
// student.sex="女"
println("name= "+student.getName+" age="+student.age+" sex="+student.sex)
}
}
class Student {
@BeanProperty //此注解只能用在非private字段
var name: String = _ //""
var age: Int = _ //默认值为0
var sex: String = _ //""
}
默认权限为public
,但是Scala中无public关键字.private
为私有权限,只在类的内部和伴生对象
中使用.protected
为受保护权限,Scala中受保护权限比Java中更严格,同类,子类
可以访问,同包无法访问.private[包名]增加包访问权限
,包名下的其他类也可以使用.private[this]
只能在当前对象内部
访问package com.package1
class Person {
private var id: String = "202001888"
protected var name: String = "tom"
var sex: String = "女"
private[com] var age: Int = 20
def printInfo():Unit={
println(s"id=$id name=$name sex=$sex age=$age")
}
}
package com.package2
import com.package1.Person
class Student extends Person{
override def printInfo(): Unit = {
name="scala"
sex="女"
age=30
println(s"name=$name age=$age sex=$sex")
}
}
import com.package2.Student
object InputDemo {
def main(args: Array[String]): Unit = {
val student = new Student()
student.printInfo()
}
}
name=scala age=30 sex=女
class 类名(形参列表){ // 主构造器
// 类体
def this(形参列表) { // 辅助构造器 }
def this(形参列表) { //辅助构造器可以有多个... }
}
object InputDemo {
def main(args: Array[String]): Unit = {
val student = new Student
println("==============================")
val student1 = new Student("tom")
println("==============================")
val student2 = new Student("alice", 38)
}
}
class Student() {
//定义属性
var name: String = _
var age: Int = _
println("1 主构造方法")
def this(name: String) { //辅助构造方法
this() // 直接调用主构造方法
println("2 辅助构造方法")
this.name = name
println(s"name:$name age:$age")
}
def this(name: String, age: Int) { //辅助构造方法
this(name) // 间接调用主构造方法
println("3 辅助构造方法")
this.age=age
println(s"name:$name age:$age")
}
}
未用任何修饰符修饰
,那么这个参数是局部变量
。val /var
关键字声明,那么 Scala 会将参数作为类的成员属性
使用object InputDemo {
def main(args: Array[String]): Unit = {
val student = new Student
val student1 = new Student4("tom", 20)
println(s"${student1.name} ${student1.age}")
}
}
class Student { //无参构造器
//定义属性
var name: String = _
var age: Int = _
}
//上面等价于
class Student2(var name: String, var age: Int)
//主构造器参数无修饰 局部变量
class Student3(name: String,age: Int)
class Student4(val name: String, val age: Int)
在Java中:
public class Person {
public static void main(String[] args) {
Student student = new Student();
System.out.println(student.name);
student.hello();
System.out.println("========================");
Person1 student1 = new Student();
System.out.println(student1.name); //静态绑定属性
student1.hello(); //动态绑定方法
}
}
class Person1 {
String name = "person";
public void hello() {
System.out.println("hello person");
}
}
class Student extends Person1 {
String name = "student";
@Override
public void hello() {
System.out.println("hello student");
}
}
student
hello student
========================
person
hello student
在Scala中:
object TestDemo {
def main(args: Array[String]): Unit = {
val student: Student = new Student
println(student.name)
student.hello()
val student2: Person = new Student
println(student2.name) //在Scala中属性和方法都是动态绑定的
student2.hello()
}
}
class Person {
val name: String = "person"
def hello(): Unit = {
println("hello person")
}
}
class Student extends Person {
override val name: String = "student"
override def hello(): Unit = {
println("hello student")
}
}
student
hello student
student
hello student
abstract class Person{}
通过abstract关键字标记抽象类var|val name:String
一个属性没有初始化就是抽象属性def hello():String
只声明而没有实现的方法就是抽象方法继承和重写:
var
修饰;子类对非抽象属性重写,父类非抽象属性只支持val
类型,而不支持var
.因为var修饰的为可变变量,子类继承之后可以直接使用,没有必要重写
object TestDemo {
def main(args: Array[String]): Unit = {
val person:Person = new Person { //匿名子类
override var name: String = "tom"
override def hello(): Unit = println(s"hello $name")
}
println(person.name)
person.hello()
}
}
abstract class Person {
var name: String //抽象属性
def hello(): Unit //抽象方法
}
object TestDemo {
def main(args: Array[String]): Unit = {
println("=========第1种方法===========")
val student = new Student("tom", 20) //利用伴生类创建对象
student.printInfo()
println("=========第2种方法===========")
val student1: Student = Student.newStudent("alice", 25) //利用伴生对象创建对象
student1.printInfo()
println("===========第3种方法==============")
val student2: Student = Student.apply("zhangsan", 13)
student2.printInfo()
// .apply可以省略不写
val student3: Student = Student("zhangsan", 13)
student2.printInfo()
}
}
class Student(val name: String, var age: Int) { //伴生类
def printInfo(): Unit = {
println(s"student: name=$name age=$age school=${Student.school}")
}
}
object Student { //单例对象(伴生对象)
val school: String = "B站大学"
def newStudent(name: String, age: Int): Student = new Student(name, age)
def apply(name: String, age: Int): Student = new Student(name, age)
}
object TestDemo {
def main(args: Array[String]): Unit = {
val student1 = Student.getInstance()
student1.printInfo()
}
}
class Student private(val name: String, var age: Int) { //私有化构造器
def printInfo(): Unit = {
println(s"student: name=$name age=$age school=${Student.school}")
}
}
//饿汉式
object Student { //单例对象(伴生对象)
val school: String = "B站大学"
private val student: Student = new Student("alice", 10)
def getInstance(): Student = student
}
object Student { //单例对象(伴生对象)
val school: String = "B站大学"
private var student: Student = _
def getInstance(): Student = {
if (student == null) {
student = new Student("alice", 20)
}
student
}
}
Scala语言中,采用特质trait(特征)来替代接口
的概念.也就是说,多个类具有相同的特质(特征)时,就可以将这个特质(特征)独立出来,采用关键字trait声明.
Scala中的trait中即可以有抽象属性和方法
,也可以有具体的属性和方法
.一个类可以混入多个特质
.这种感觉类似于Java中的抽象类.
Scala引入trait特征,第一可以替代Java的接口,第二个也是对单继承机制的一种补充.
trait 特质名{
}
特质基本语法:一个类具有某种特质(特征),就意味着这个类满足了这个特质(特征)的所有要素.所以在使用时,也采用了extends关键字
.如果有多个特质或存在父类,那么需要采用with关键字
连接.
class 类名 extends 特质1 with 特质2 with 特质3...
class 类名 extends 父类 with 特质1 with 特质2 with 特质3...
说明:
object TestDemo {
def main(args: Array[String]): Unit = {
val student = new Student
student.sayHello()
student.study()
student.play()
}
}
//定义一个特质
trait Yong {
//声明抽象和非抽象属性
var age: Int
val name: String = "yong"
//声明抽象和非抽象方法
def study(): Unit
def play(): Unit = {
println("yong people is playing")
}
}
class Person {
val name: String = "person"
var age: Int = 20
def sayHello(): Unit = {
println(s"${name}在跟你说hello")
}
}
class Student extends Person with Yong {
//重写冲突的属性
override val name: String = "student"
//实现抽象方法
def study(): Unit = {
println(s"student $name is studying")
}
//重写父类方法
override def sayHello(): Unit = {
println(s"$name is say hello")
}
除了可以在类声明时继承特质以外,还可以在构建对象时混入特质
object TestDemo {
def main(args: Array[String]): Unit = {
//创建 OracleDB 实例,同时动态混入 Operate3 特质
val oracleDB = new OracleDB with Operate3 {
override def insert2(): Unit = {
println("insert2")
}
}
oracleDB.insert(100)
oracleDB.insert2()
val mySQL3 = new MySQL3 with Operate3 {
override def insert2(): Unit = {
println("============mySQL3 insert2 =========")
}
override def sayHi: Unit = {
println("mySQL3 sayHi")
}
}
mySQL3.insert2()
mySQL3.sayHi
//如果我们要去实例化一个 abstract 类,也可以,但是需要时候用匿名子类来构建
val mySQL = new MySQL3 {
override def sayHi: Unit = {
println("hi")
}
}
}
}
//特质
trait Operate3 {
def insert(id: Int): Unit = {
println("插入数据 = " + id)
}
def insert2(): Unit
}
//普通类
class OracleDB {}
//抽象类
abstract class MySQL3 {
def sayHi(): Unit
}
构建对象的同时如果混入多个特质,称之为叠加特质,那么特质声明顺序从左到右
,方法执行顺序从右到左
。
object TestDemo {
def main(args: Array[String]): Unit = {
val student = new Student
student.increase()
}
}
class Person{
def increase(): Unit={
println("Person increase")
}
}
trait Knowledge {
var amount: Int = 0
def increase(): Unit={
println("knowledge increase")
}
}
trait Talent{
def singing():Unit
def dancing():Unit
def increase(): Unit={
println("talent increase")
}
}
class Student extends Person with Knowledge with Talent{
override def dancing(): Unit = {
println("dancing")
}
override def singing(): Unit = println("singing")
override def increase(): Unit = super.increase() //重写冲突方法
}
talent increase
一个类(Sub)混入的两个trait(traitA,traitB)中具有相同的具体方法,且两个trait继承自相同的trait(traitC),及所谓的"钻石问题",解决这类冲突问题,Scala采用了特质叠加的策略.
所谓的特质叠加,就是将混入的多个trait中的冲突方法叠加起来.
如果想要调用具体特质的方法,可以指定:super[特质].xxx(…).其中的泛型必须是该特质的直接超类类型
object TestDemo {
def main(args: Array[String]): Unit = {
val ball = new MyFootBall
println(ball.describe()) //my ball is foot-red-ball
}
}
trait Ball {
def describe(): String = "ball"
}
trait ColorBall extends Ball {
var color: String = "red"
override def describe(): String = color + "-" + super.describe() //super.describe()调用的是Ball特质的describe()方法
}
trait CategoryBall extends Ball {
var category: String = "foot"
override def describe(): String = category + "-" + super.describe() //super.describe()调用的是ColorBall特质的describe()方法
// override def describe(): String = category + "-" + super[Ball].describe() 指定调用Ball特质的describe()方法
}
class MyFootBall extends ColorBall with CategoryBall {
override def describe(): String = "my ball is " + super.describe() //重写冲突的方法
}
object TestDemo {
def main(args: Array[String]): Unit = {
val user = new RegisterUser("tom", "123456")
user.insert()
println(user.passwd+" "+user.name)
}
}
//用户类
class User(val name: String, val passwd: String)
//自身类型特质
trait UserDao {
// 明确告诉编译器,我就是 User,如果没有这句话,下面的name不能使用
_: User => //定义自身类型
def insert(): Unit = {
//既然我就是User,那么我就可以使用name
println(s"insert into database:$name")
}
}
class RegisterUser(name: String, passwd: String) extends User(name, passwd) with UserDao
/*insert into database:tom
123456 tom*/
obj.isInstanceOf[T]
判断obj是不是T类型
obj.asInstanceOf[T]
将obj强转成T类型
classOf
获取对象的类名
object TestDemo {
def main(args: Array[String]): Unit = {
val student = new Student("alice", 20)
student.study() //student study
student.sayHi() //hi from student alice
val person: Person = new Student("tom", 88)
person.sayHi() //hi from student tom
println(student.isInstanceOf[Person]) //true
println(person.isInstanceOf[Student]) //true
println(person.isInstanceOf[Person]) //true
if (person.isInstanceOf[Student]) {
val newStudent = person.asInstanceOf[Student]
newStudent.study() //student study
}
println(classOf[Student]) //class com.package2.Student
}
}
class Person(val name: String, val age: Int) {
def sayHi(): Unit = {
println("hi from person " + name)
}
}
class Student(name: String, age: Int) extends Person(name, age) {
override def sayHi(): Unit = {
println("hi from student " + name)
}
def study(): Unit = {
println("student study")
}
}
枚举类:需要继承Enumeration
应用类:需要继承App
object TestDemo {
def main(args: Array[String]): Unit = {
println(Color.RED) //red
println(Color.BLUE) //blue
println(Color.BLACK) //BLACK
println(Color.GREEN) //green
}
}
//枚举类
object Color extends Enumeration{
val RED=Value(1,"red")
val YELLOW=Value(2,"yellow")
val BLUE=Value(3,"blue")
val BLACK=Value(4)
val GREEN=Value("green")
}
//应用类
object TestApp extends App{
println("hello scala") //hello scala
}
序列 Seq、集 Set、映射 Map
,所有的集合都扩展自 Iterable 特质,在 Scala 中集合 有可变(mutable)和不可变(immutable)两种类型.默认采用不可变集合
,对于几乎所有的集合类,Scala 都同时提供了可变(mutable)和不可变(immutable) 的版本(1)定义数组:
val arr1 = new Array[Int](10)
Any
val aar2=Array(11,22,3,34,66,77)
调用伴生对象的apply()方法创建数组(2)通过数组名.length
或者数组名.size
来获取数组的长度.
object TestDemo {
def main(args: Array[String]): Unit = {
val arr01 = new Array[Int](4) // //创建一个数组,存放Int类型的数据,大小为4
println(arr01.length) // 4
println("arr01(0)=" + arr01(0)) //访问指定下标的元素
for (i <- arr01) { //遍历数组
print(i+" ")
}
println()
arr01(3) = 10 //修改元素
println(arr01.mkString(" "))
}
}
遍历集合
object TestDemo {
def main(args: Array[String]): Unit = {
val arr = Array(11, 22, 33, 44, 55, 66, 77, 88)
for (i <- 0 until arr.length) {
println(arr(i))
}
//等价代码 因为 def indices: Range = 0 until length
for (i <- arr.indices) {
println(arr(i))
}
//迭代器
val iterator = arr.iterator
while (iterator.hasNext){
println(iterator.next())
}
//调用foreach()方法
// arr.foreach((elem: Int) =>println(elem))
arr.foreach(println)
}
}
object TestDemo {
def main(args: Array[String]): Unit = {
val arr = Array(22, 33, 44, 55, 66, 77, 88)
//在数组最后面添加一个元素
val newArr = arr.:+(99)
println(newArr.mkString(" "))
//在数组最前面添加一个元素
val newArr2 = arr.+:(11)
println(newArr2.mkString(" "))
//可以去掉.点号 用空格隔开 也可以去掉小括号
val newArr3 = arr :+ 99
println("newArr和newArr3等价: " + newArr3.mkString(" "))
val newArr4 = 11 +: arr //注意不能这样子写arr +: 11
println("newArr2和newArr4等价: " + newArr4.mkString(" "))
//一次性添加多个元素
val newArr5 = 1 +: 2 +: 3 +: arr :+ 4 :+ 5 :+ 6
println(newArr5.mkString(" ")) //1 2 3 22 33 44 55 66 77 88 4 5 6
}
}
(1)定义可变数组:
val/var 变量名 =new ArrayBuffer[元素类型]()
val/var 变量名 = ArrayBuffer(元素1,元素2,元素3....)
(2)
+=
添加单个元素-=
删除单个元素++=
追加一个数组到可变数组中--=
移除可变数组中的指定多个元素object TestDemo {
def main(args: Array[String]): Unit = {
//创建可变数组
val arr:ArrayBuffer[Int] = new ArrayBuffer[Int]()
val arr2 =ArrayBuffer(22,33,44,55,66)
println(arr) //ArrayBuffer()
println(arr2) //ArrayBuffer(22, 33, 44, 55, 66)
//访问元素
println(arr2(3))
// 修改元素
arr2(2)=66
println(arr2) //ArrayBuffer(22, 33, 66, 55, 66)
}
}
object TestDemo {
def main(args: Array[String]): Unit = {
//创建可变数组
val arr: ArrayBuffer[Int] = new ArrayBuffer[Int]()
val arr2 = ArrayBuffer(22, 33, 44, 55, 66)
//添加元素
//val newArr = arr2.:+(99)
val newArr = arr2 :+ 99
println(newArr) //ArrayBuffer(22, 33, 44, 55, 66, 99)
val newArr2 = arr
//在数组最后面添加一个元素
arr += 1
println(arr) //ArrayBuffer(1)
//往数组里添加元素,数组的引用不变
println(arr.eq(newArr2)) //true
println(newArr2) //ArrayBuffer(1)
newArr2 += 2
println(arr) //ArrayBuffer(1, 2)
//在数组最前面添加一个元素
3 +=: arr
println(arr) //ArrayBuffer(3, 1, 2)
//调用方法 在数组最前面添加一个元素
arr.prepend(0)
// 在数组最后面添加一个元素
arr.append(9)
//一次性添加多个元素
arr.append(10,12,13)
println(arr) //ArrayBuffer(0, 3, 1, 2, 9)
//在指定索引处插入(多个)元素
arr.insert(3,66,77,88)
println(arr) //ArrayBuffer(0, 3, 1, 66, 77, 88, 2, 9, 10, 12, 13)
//在指定索引处将arr2的所有元素插入
arr.insertAll(1,arr2)
println(arr)
// arr.prependAll(arr2)
// arr.appendAll(arr2)
//删除指定索引处的元素
arr.remove(6)
//从指定索引0处开始连续删除5个元素
arr.remove(0,5)
println(arr)
arr -= 66 //只删除第一次出现的 66
println(arr)
arr.clear() //清空数组
println(arr) //ArrayBuffer()
}
}
object TestDemo {
def main(args: Array[String]): Unit = {
//创建可变数组
val arr: ArrayBuffer[Int] = ArrayBuffer(22, 33, 44, 55, 66)
//创建不可变数组
val arr1: Array[Int] = Array(1, 2, 3, 4, 5, 6)
//将可变数组转变为不可变数组
val newArr = arr.toArray
println(newArr.mkString(","))
//将不可变数组转变为可变数组
val newArr2: mutable.Buffer[Int] = arr1.toBuffer
println(newArr2)
}
}
object TestDemo {
def main(args: Array[String]): Unit = {
//创建二维数组
val array: Array[Array[Int]] = Array.ofDim(2, 3)
//创建三维数组
val array1: Array[Array[Array[Int]]] = Array.ofDim(2, 3, 4)
//访问元素
array(0)(0) = 1
array(1)(1) = 1
//遍历数组
for {i <- array.indices
j <- array(i).indices} {
print(array(i)(j) + " ")
if (j == array(i).length - 1) {
println()
}
}
array.foreach(line => line.foreach(println))
//简写
array.foreach(_.foreach(println))
}
}
(1)不可变列表指的是: 列表的元素、长度都是不可变的
。
(2)定义不可变列表:
val/var 变量名 = List(元素1, 元素2, 元素3...)
Nil
创建一个空列表.val/var 变量名 = Nil
::
方法实现.val/var 变量名 = 元素1 :: 元素2 :: Nil
::
拼接方式来创建列表,必须在最后添加一个Nil(3)
object TestDemo {
def main(args: Array[String]): Unit = {
//创建一个List 注意:不能用List的构造方法创建List,因为List是个抽象类 只能用它的伴生对象创建List
val list = List(11, 22, 33)
println(list) //List(11, 22, 33)
// list(1)=66 注意:不能更改它的值
//添加元素
val newList1 = list.:+(44)
// val newList = list :+ 44
val newList2 = list.+:(0)
// val newList2 = 0 +: list
println(newList1) //List(11, 22, 33, 44)
println(newList2) //List(0, 11, 22, 33)
// val newList3 = list.::(55)
val newList3 = 55 :: list
println(newList3) //List(55, 11, 22, 33)
val list2 = Nil.::(1)
println(list2) //List(1)
// 1) 符号::表示向集合中 新建集合添加元素。 2) 运算时,集合对象一定要放置在最右边, 3) 运算规则,从右向左
val list3 = 1 :: 2 :: 3 :: 4 :: Nil //第二种创建List的方法
println(list3) //List(1, 2, 3, 4)
//合并列表
val list4 = list ::: list3
println(list4) //List(11, 22, 33, 1, 2, 3, 4)
val list5 = list ++ list3
println(list5) //List(11, 22, 33, 1, 2, 3, 4)
}
}
列表的元素、长度
都是可变的.val/var 变量名 = ListBuffer[数据类型]()
val/var 变量名 = ListBuffer(元素1,元素2,元素3...)
object TestDemo {
def main(args: Array[String]): Unit = {
//创建可变列表
val list1: ListBuffer[Int] = new ListBuffer[Int]()
val list2: ListBuffer[Int] = ListBuffer(11, 22, 33)
println(list1) //ListBuffer()
println(list2) //ListBuffer(11, 22, 33)
//添加元素
list1.append(1, 2, 3)
list1.prepend(-1, 0)
//在指定索引1处添加(1个或多个)元素
list1.insert(1, 66, 77)
println(list1) //ListBuffer(-1, 66, 77, 0, 1, 2, 3)
11 +=: 22 +=: 33 +=: list1 += 44 += 55
println(list1) //ListBuffer(11, 22, 33, -1, 66, 77, 0, 1, 2, 3, 44, 55)
//从指定索引3处开始连续删除5个元素
list1.remove(3, 5)
println(list1) //ListBuffer(11, 22, 33, 2, 3, 44, 55)
//合并列表
val list3 = list1 ++ list2
println(list3) //ListBuffer(11, 22, 33, 2, 3, 44, 55, 11, 22, 33)
list1 ++= list2 //list1调用++=方法
println(list1) //ListBuffer(11, 22, 33, 2, 3, 44, 55, 11, 22, 33)
//清空列表
list1.clear()
list1.append(1, 2, 3, 4, 5, 6)
list1 ++=: list2 //list2调用++=:方法
println(list1) //ListBuffer(1, 2, 3, 4, 5, 6)
println(list2) //ListBuffer(1, 2, 3, 4, 5, 6, 11, 22, 33)
//修改元素
list1(0) = 0
list1.update(1, 66)
println(list1) //ListBuffer(0, 66, 3, 4, 5, 6)
//删除元素
list1.remove(1)
list1 -= 5
println(list1) //ListBuffer(0, 3, 4, 6)
}
}
拉链
:将两个列表,组合成一个元素为元组的列表拉开
:将一个包含元组的列表,拆解成包含两个列表的元组object TestDemo2 {
def main(args: Array[String]): Unit = {
//1. 定义列表names, 保存三个学生的姓名,分别为:张三、李四、王五
val names = List("张三", "李四", "王五")
// 2. 定义列表ages, 保存4个学生的年龄,分别为:23, 24, 25,30
val ages = List(23, 24, 25,30)
// 3. 使用zip将列表names和ages, 组合成一个元素为元组的列表list1.
val list1 = names.zip(ages)
// 4. 使用unzip将列表list1拆解成包含两个列表的元组tuple1
val tuple1 = list1.unzip
// 5. 打印结果
println("拉链: " + list1)
println("拉开: " + tuple1)
}
}
元素, 集的长度都不可变
.val/var 变量名 = Set[类型]()
val/var 变量名 = Set(元素1, 元素2, 元素3...)
不可变集的常见操作 :
size
)+
)++
)++
) -
(减号)表示删除一个元素, 生成一个新的Set--
表示批量删除某个集中的元素, 从而生成一个新的Setobject TestDemo {
def main(args: Array[String]): Unit = {
//创建不可变Set
val set = Set(11, 22, 33, 44, 55)
println(set)
// 添加元素
// val set1 = set.+(20)
val set1 = set + 20
println(set1)
//合并Set
val set2 = Set(1, 2, 3, 4)
val set3 = set1 ++ set2
println(set3)
//删除元素
val set4 = set3 - 55
println(set4)
}
}
可变集指的是元素, 集的长度
都可变, 它的创建方式和不可变集的创建方式一致,只不过需要先导入可变集类.
object TestDemo {
def main(args: Array[String]): Unit = {
//创建可变Set
val set = mutable.Set(1, 2, 3, 4, 2, 3)
println(set)
//添加元素
val set2 = set + 66
println(set2)
set += 11
println(set)
val b: Boolean = set.add(22)
println(set)
val b1: Boolean = set.add(22)
//true表示添加成功 false表示添加失败
println(b + "\t" + b1) //true false
//删除元素
set -= 1
println(set)
val bool: Boolean = set.remove(2)
val bool1: Boolean = set.remove(2)
//true表示删除成功 false表示删除失败
println(bool + "\t" + bool1)
//合并集合
val set3 = set ++ set2
println(set3)
set ++= set2
println(set)
}
}
映射
指的就是Map。它是由键值对(key, value)
组成的集合。特点是: 键具有唯一性
, 但是值可以重复
.
在Scala中,Map也分为不可变Map和可变Map
。
注意: 如果添加重复元素(即: 两组元素的键相同), 则会用新值覆盖旧值.
箭头
的方式实现.val/var map = Map(键->值, 键->值, 键->值...)
// 推荐,可读性更好val/var map = Map((键, 值), (键, 值), (键, 值), (键, 值)...)
object TestDemo {
def main(args: Array[String]): Unit = {
//创建不可变Map
val map: Map[String, Int] = Map("a" -> 1, "b" -> 2)
println(map)
println(map.getClass) //class scala.collection.immutable.Map$Map2
//遍历map
map.foreach((kv: (String, Int)) => println(kv))
map.foreach(println)
//取map中所有的key
for(key <- map.keys){
println(s"$key ----> ${map.get(key)}") //a ----> Some(1)等
}
//获取某一个key的value值
println(map.get("b").get) //2
println(map.get("c")) //None
println(map.getOrElse("c","默认值")) //默认值
println(map.getOrElse("a","默认值")) //1
println(map("a")) //1
}
}
可变Map指的是元素, 长度都可变.
定义语法与不可变Map一致, 只不过需要先手动导包:import scala.collection.mutable.Map
object TestDemo {
def main(args: Array[String]): Unit = {
//创建可变Map
val map: mutable.Map[String, Int] = mutable.Map("a" -> 1, "b" -> 2)
println(map.getClass) //class scala.collection.mutable.HashMap
//添加元素
map.put("c", 3)
map.put("d", 4)
println(map)
map += (("e", 5)) //注意:双重括号
println(map)
//删除元素
map.remove("a")
map -= "c"
println(map)
//修改元素
map.update("e", 66)
println(map) //Map(e -> 66, b -> 2, d -> 4)
//合并Map
//创建不可变Map
val map1: Map[String, Int] = Map("a" -> 1, "b" -> 99)
map ++= map1 //如果map当中已经有对应的key了,就覆盖对应的value值
println(map) //Map(e -> 66, b -> 99, d -> 4, a -> 1)
}
}
map(key)
: 根据键获取其对应的值, 键不存在返回None.map.keys
: 获取所有的键.map.values
: 获取所有的值.getOrElse
: 根据键获取其对应的值, 如果键不存在, 则返回指定的默认值.+号: 增加键值对
, 并生成一个新的Map.注意: 如果是可变Map, 则可以通过+=或者++=直接往该可变Map中添加键值对元素.
-号
: 根据键删除
其对应的键值对元素, 并生成一个新的Map.注意: 如果是可变Map, 则可以通过-=或者--=直接从该可变Map中删除键值对元素.
元组可以理解为一个容器,可以存放各种相同或不同类型的数据.说的简单点,就是将多个无关的数据封装成一个整体,称为元组.
元组中最大只能有22个元素
Map中的键值对其实就是元组
,只不过元组的元素个数为2,称之为对偶.
元组的长度和元素都是不可变的.
val/var 元组 = (元素1, 元素2, 元素3....)
val/var 元组 = 元素1->元素2
元组名._编号
的形式来访问元组中的元素,_1表示访问第一个元素
,依次类推.也可以通过元组名.productIterator
的方式, 来获取该元组的迭代器, 从而实现遍历元组.object TestDemo {
def main(args: Array[String]): Unit = {
//创建元组
val tuple: (String, Int, Boolean, Char) = ("hello", 66, true, 'a')
println(tuple)
//访问数据
println(tuple._1) //hello
println(tuple._2) //66
println(tuple._3) //true
println(tuple._4) //a
println(tuple.productElement(1)) //66 下标是从 0 开始计算
//遍历元组[通过迭代器来遍历]
for (elem <- tuple.productIterator) {
println(elem)
}
//嵌套元组
val mulTuple: (Int, String, Char, (String, Int)) = (66, "hello", 'a', ("scala", 88))
println(mulTuple._4._1) //scala
}
}
概述 :
Scala针对每一类集合都提供了一个迭代器(iterator), 用来迭代访问集合.
object TestDemo2 {
def main(args: Array[String]): Unit = {
//1. 定义一个列表,包含以下元素:1,2,3,4,5
val list1 = List(1, 2, 3, 4, 5)
// 2. 使用while循环和迭代器,遍历打印该列表.
// 2.1 根据列表获取其对应的迭代器对象.
val it = list1.iterator
// 2.2 判断迭代器中是否有下一个元素.
while (it.hasNext) {
// 2.3 如果有, 则获取下一个元素, 并打印.
println(it.next)
}
// 迭代完后, 再次使用该迭代器获取元素, 则抛异常: NoSuchElementException
// println(it.next)
}
}
获取集合的长度length
获取集合的大小size
是否包含contains(数据)
求和sum
求乘积product
求最大值max
求最小值min
object TestDemo {
def main(args: Array[String]): Unit = {
val list = List(("a", 32), ("hello", 56), ("scala", 66), ("spack", 2), ("java", 23))
val list1 = List(12, 43, 65, 2, 12, 65, 32, 3)
//求最大值
println(list.max) //(spack,2)
// 求乘积
println(list1.product)
// 求和
println(list1.sum)
// 集合的长度
println(list1.length) //8
// 集合的大小
println(list1.size) //8
println(list.maxBy((tuple: (String, Int)) => tuple._2))
println(list.maxBy(_._2)) //(scala,66)
//排序
val sorted = list1.sorted
println(sorted) //从小到大
println(sorted.reverse) //从大到小
//传入隐式参数
println(list1.sorted(Ordering[Int])) //默认从小到大
println(list1.sorted(Ordering[Int].reverse)) //从大到小
println(list.sorted)
println(list.sortBy(_._2))
println(list.sortBy(_._2)(Ordering[Int].reverse))
println(list1.sortWith((a: Int, b: Int) => { a < b }))
println(list1.sortWith(_ < _)) //从小到大
println(list1.sortWith(_ > _)) //从大到小
}
}
object TestDemo {
def main(args: Array[String]): Unit = {
//创建不可变列表
val list = List(1, 3, 5, 7, 9, 11)
val list2 = List(1, 22, 7, 44, 3, 11, 8, 7)
//获取集合的头
println(list.head) //1
//获取集合的尾(不是头的就是尾)
println(list.tail) //List(3, 5, 7, 9, 11)
//获取集合最后一个元素
println(list.last) //11
//获取集合初始数据(不包含最后一个元素)
println(list.init) //List(1, 3, 5, 7, 9)
//反转
println(list.reverse) //List(11, 9, 7, 5, 3, 1)
//取前(后)n个元素
println(list.take(3)) //List(1, 3, 5)
println(list.takeRight(3)) //List(7, 9, 11)
//去掉前(后)n个元素
println(list.drop(2)) //List(5, 7, 9, 11)
println(list.dropRight(3)) //List(1, 3, 5)
//并集
val union = list.union(list2)
println(union) //List(1, 3, 5, 7, 9, 11, 1, 22, 7, 44, 3, 11, 8, 7)
println(list:::list2) //List(1, 3, 5, 7, 9, 11, 1, 22, 7, 44, 3, 11, 8, 7)
//交集
val inter = list.intersect(list2) //List(1, 3, 7, 11)
println(inter)
//差集
val diff1 = list.diff(list2)
println(diff1) //List(5, 9)
//拉链
val tuples:List[(Int,Int)] = list.zip(list2)
println(tuples) //List((1,1), (3,22), (5,7), (7,44), (9,3), (11,11))
//滑窗
for(elem <- list.sliding(3)){ //窗口的大小为3,默认步长为1
println(elem)
}
println("===================")
for(elem <- list.sliding(3,2)){ //窗口大小为3,步长为2
println(elem)
}
}
}
List(1, 3, 5)
List(3, 5, 7)
List(5, 7, 9)
List(7, 9, 11)
===================
List(1, 3, 5)
List(5, 7, 9)
List(9, 11)
过滤
:遍历一个集合并从中获取满足指定条件的元素组成一个新的集合转化/映射(map)
:将集合中的每一个元素映射到某一个函数扁平化
扁平化+映射
注:flatMap相当于先进行map操作,再进行flatten操作,集合中的每个元素的子元素映射到某个函数并返回新集合分组(group)
:按照指定的规则对集合的元素进行分组简化(规约)
折叠
:fold 函数将上一步返回的值作为函数的第一个参数继续传递参与运算,直到 list 中的所有元素被遍历将一个列表中的数据合并为一个
.package testpackage
object TestDemo2 {
def main(args: Array[String]): Unit = {
val list = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
// 1.过滤 (选取偶数)
// val evenList = list.filter((elem: Int) => { elem % 2 == 0 })
val eventList = list.filter(_ % 2 == 0)
println(eventList) //List(2, 4, 6, 8)
// 2. map (将集合中的每个元素乘2)
//val list1 = list.map((elem: Int) => { elem * 2 })
val list1 = list.map(_ * 2)
println(list1) // List(2, 4, 6, 8, 10, 12, 14, 16, 18)
// 求平方
val list2 = list.map(x => x * x)
println(list2) //List(1, 4, 9, 16, 25, 36, 49, 64, 81)
// 3.扁平化
val list3: List[List[Int]] = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9))
val list4 = list3(0) ::: list3(1) ::: list3(2)
val list5 = list3(0) ++ list3(1) ++ list3(2)
println(list4) //List(1, 2, 3, 4, 5, 6, 7, 8, 9)
println(list5) //List(1, 2, 3, 4, 5, 6, 7, 8, 9)
val flatList = list3.flatten
println(flatList) //List(1, 2, 3, 4, 5, 6, 7, 8, 9)
// 4.扁平化+映射 (将一组字符串进行分词,并保存成单词的列表)
val strings: List[String] = List("hello scala", "hello spark", "hello flume", "hello kafka", "we study")
val splitList: List[Array[String]] = strings.map(_.split(" ")) //分词
println(splitList.flatten) //List(hello, scala, hello, spark, hello, flume, hello, kafka, we, study)
println("==============================")
val flatmapList = strings.flatMap(_.split(" "))
println(flatmapList) //List(hello, scala, hello, spark, hello, flume, hello, kafka, we, study)
// 5.分组 (分成奇偶两组) Map的key是组名,value是这一组中的所有元素
val groupMap: Map[Int, List[Int]] = list.groupBy(_ % 2) // 组名: 0 1
val groupMap2: Map[String, List[Int]] = list.groupBy(elem => {
if (elem % 2 == 0) "偶数" else "奇数"
}) //组名(两组): 偶数 奇数
println(groupMap) //Map(1 -> List(1, 3, 5, 7, 9), 0 -> List(2, 4, 6, 8))
println(groupMap2) //Map(奇数 -> List(1, 3, 5, 7, 9), 偶数 -> List(2, 4, 6, 8))
println("================================")
//给定一组词汇,按照单词的首字母进行分组
val wordList: List[String] = List("java", "scala", "hadoop", "kafka", "flume", "hbase", "spark", "flink")
val charToStrings: Map[Char, List[String]] = wordList.groupBy(_.charAt(0))
println(charToStrings) //Map(s -> List(scala, spark), j -> List(java), f -> List(flume, flink), h -> List(hadoop, hbase), k -> List(kafka))
}
}
package testpackage
object TestDemo2 {
def main(args: Array[String]): Unit = {
//1. def reduceLeft(op: (B, A) => B): B
//2. reduceLeft 高阶函数,接收的函数 op 接收两个形参
//3. op 返回的结果,会作为下一次调用 op 时的第一个参数(左边),再次传入
//4. 当把整个 list 的集合都遍历后,计算结束(递归)
val list = List(1, 2, 3, 4)
// 规约
// list.reduce((a:Int,b:Int) => a + b)
val sum: Int = list.reduce(_ + _) //从左往右
println(sum) //10
// 从左往右加
println(list.reduceLeft(_+_)) //10
// 从右往左加
println(list.reduceRight(_+_)) //10
val list1 = List(3, 4, 5, 8, 10)
println(list1.reduce(_-_)) //-24 3-4-5-8-10=-24
println(list1.reduceLeft(_-_)) //-24 3-4-5-8-10=-24
println(list1.reduceRight(_-_)) //6 3-(4-(5-(8-10)))=6
/* 源码:
def reduceRight[B >: A](op: (A, B) => B): B =
if (isEmpty) throw new UnsupportedOperationException("Nil.reduceRight")
else if (tail.isEmpty) head
else op(head, tail.reduceRight(op))
*/
}
}
package testpackage
object TestDemo2 {
def main(args: Array[String]): Unit = {
//求最小值
val list = List(3, 4, 2, 7, 5, -1, 8, 90, 999)
//val minVal = list.reduce((n1: Int, n2: Int) => { if (n1 > n2) n2 else n1 })
val minVal = list.reduce((n1, n2) => if (n1 > n2) n2 else n1 )
println("minVal=" + minVal) // -1
}
}
package testpackage
object TestDemo2 {
def main(args: Array[String]): Unit = {
val list=List(1,2,3,4,5,6,7,8,9)
val list2=List(3,4,5,8,10)
//初始聚合状态10
val a: Int = list.fold(10)(_ + _)
println(a) //55 10+1+2+3+4+5+6+7+8+9=55
println(list.foldLeft(10)(_-_)) //-35 10-1-2-3-4-5-6-7-8-9
println(list.foldRight(10)(_-_)) //-5 (9-(8-(7-(6-(5-(4-(3-(2-(1-10)))))))))=-5
println(list2.foldRight(11)(_-_)) //-5 (10-(8-(5-(4-(3-11)))))=-5
}
}
package testpackage
import scala.collection.mutable
object TestDemo2 {
def main(args: Array[String]): Unit = {
val map1 = Map("a" -> 1, "b" -> 3, "c" -> 6)
val map2 = mutable.Map("a" -> 6, "b" -> 2, "c" -> 9, "d" -> 3) //必须是可变Map
println(map1 ++ map2) //Map(a -> 6, b -> 2, c -> 9, d -> 3)
// 遍历map1里面的每一个元素,去判断key是否在map2中,更新map2的值,所以map2必须是可变Map
//map2初始聚合状态 mergeMap当前聚合状态
/*注意:不能使用flod 因为
def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = foldLeft(z)(op)
map2和merageMap的类型都是Map,而kv是元组,类型不一致
*/
// def foldLeft[B](z: B)(op: (B, A) => B): B
val map3 = map1.foldLeft(map2)((mergeMap, kv) => {
val key: String = kv._1
val value = kv._2
mergeMap(key) = mergeMap.getOrElse(key, 0) + value
mergeMap
})
println(map3) //Map(b -> 5, d -> 3, a -> 7, c -> 15)
println(map2) //Map(b -> 5, d -> 3, a -> 7, c -> 15)
}
}
package testpackage
object TestDemo2 {
def main(args: Array[String]): Unit = {
val stringList: List[String] = List("hello java", "hello scala", "hello spark", "hello hadoop zookeeper", "hello scala flink", "scala flink flume")
// 1.对字符串进行切分,得到一个打散所有单词的列表
// val wordList1: List[Array[String]] = stringList.map(_.split(" "))
// val wordList2: List[String] = wordList1.flatten
// println(wordList2)
val strings: List[String] = stringList.flatMap(_.split(" "))
println(strings)
//2.相同的单词进行分组(注意:此时不能将其改成_,因为编译器容易出现混淆)
val groupMap: Map[String, List[String]] = strings.groupBy(word => word)
println(groupMap)
//3.对分组之后的List取长度,得到每个单词的个数
val countMap: Map[String, Int] = groupMap.map(kv => (kv._1, kv._2.length))
//4.将Map转换为List,并排序取前3
// val list: List[(String, Int)] = countMap.toList
// val sortList: List[(String, Int)] = list.sortWith(_._2 > _._2)
// val result: List[(String, Int)] = sortList.take(3)
// println(result) //List((hello,5), (scala,3), (flink,2))
val result: List[(String, Int)] = countMap.toList
.sortWith(_._2 > _._2)
.take(3)
println(result)
}
}
package testpackage
object TestDemo2 {
def main(args: Array[String]): Unit = {
val stringList: List[(String, Int)] = List(
("hello java", 2), ("hello scala", 3),
("hello spark", 2), ("hello hadoop zookeeper", 4),
("hello scala flink", 2), ("scala flink flume", 1)
)
//思路1:直接展开为普通版本
val strings: List[String] = stringList.map(kv => {
(kv._1.trim + " ") * kv._2
})
println(strings) //List(hello java hello java , hello scala hello scala hello scala , hello spark hello spark , hello hadoop zookeeper hello hadoop zookeeper hello hadoop zookeeper hello hadoop zookeeper , hello scala flink hello scala flink , scala flink flume )
//接下来操作与普通版本一致
val result: List[(String, Int)] = strings
.flatMap(_.split(" ")) //空格分词
.groupBy(word => word) //按照单词分组
.map(kv => (kv._1, kv._2.size)) //统计出每个单词的个数
.toList
.sortBy(_._2)(Ordering[Int].reverse)
.take(3)
println(result) //List((hello,13), (scala,6), (hadoop,4))
println("=======================================================")
//思路2:直接基于预统计的结果进行转换
//1.将字符串打散为单词,并结合对应的个数包装成二元组
val tuples: List[(String, Int)] = stringList.flatMap(tuple => {
val strings1: Array[String] = tuple._1.split(" ")
strings1.map(word => (word, tuple._2))
})
println(tuples) //List((hello,2), (java,2), (hello,3), (scala,3), (hello,2), (spark,2), (hello,4), (hadoop,4), (zookeeper,4), (hello,2), (scala,2), (flink,2), (scala,1), (flink,1), (flume,1))
//2.对二元组按照单词进行分组
val preCountMap: Map[String, List[(String, Int)]] = tuples.groupBy(_._1)
println(preCountMap)
//3.叠加每个单词预统计的个数值 key不变,更新value
val countMap: Map[String, Int] = preCountMap.mapValues(tupList => tupList.map(_._2).sum)
println(countMap) //Map(java -> 2, flink -> 3, hadoop -> 4, spark -> 2, scala -> 6, zookeeper -> 4, flume -> 1, hello -> 13)
//4.转换成List,排序取前3
val countLIst: List[(String, Int)] = countMap
.toList
.sortWith(_._2 > _._2)
.take(3)
println(countLIst) //List((hello,13), (scala,6), (hadoop,4))
}
}
Queue
的数据结构,队列的特点是先进先出
.进队和出队的方法分别为enqueue
和dequeue
有序列表
,在底层可以用数组或是链表来实现。package testpackage
import scala.collection.immutable.Queue
import scala.collection.mutable
object TestDemo2 {
def main(args: Array[String]): Unit = {
//创建一个可变队列
val que: mutable.Queue[String] = new mutable.Queue[String]()
//enqueue 的操作,表示给队列尾部加入数据
que.enqueue("a","b","c","d")
println(que) //Queue(a, b, c, d)
//dequeue 的操作,是出队列(即将队列的头元素取出)
println(que.dequeue()) //a
println(que) //Queue(b, c, d)
println(que.dequeue()) //b
println(que) //Queue(c, d)
println(que.dequeue()) //c
val str: String = que.dequeue()
println(str) //d
println(que) //Queue()
println("===========================================")
//创建一个不可变队列
val que2: Queue[String] = Queue("a", "b", "c") //不能new 创建不可变队列
val newQue: Queue[String] = que2.enqueue("d")
println(que2) //Queue(a, b, c)
println(newQue) //Queue(a, b, c,d)
}
}
多核CPU
,提供了并行集合(有别于前面的串行集合),用于多核环境的并行计算.分解为很多的子任务
,分发
给一些处理器去完成,并将它们处理的结果合并
返回 .完成自己的所有任务 之后,发现其他人还有活没干完,主动(或被安排)帮他人一起干
,这样达到尽早干完的目的package testpackage
import scala.collection.immutable
object TestDemo2 {
def main(args: Array[String]): Unit = {
//串行化执行
val result1: immutable.IndexedSeq[Long] = (0 to 100).map { x => Thread.currentThread.getId }.distinct
//并行执行
val result2 = (0 to 100).par.map { x => Thread.currentThread.getId }.distinct
println(result1) //Vector(1)
println(result2) //ParVector(12, 22, 18, 14, 17, 20, 19, 23, 21, 16, 13, 15)
}
}
package testpackage
object TestDemo2 {
def main(args: Array[String]): Unit = {
// 普通实现
val result: Option[Int] = divide(10, 2)
println(result) //Some(5)
//通过模式匹配实现
result match {
case None => println("除数不能为0")
case Some(x) => println(s"商为$x") //商为5
}
//通过getOrElse()实现
println(result.getOrElse("除数不能为0")) //5
}
// 定义一个两数相除的方法,使用Option类型来封装结果
def divide(a: Int, b: Int): Option[Int] = {
if (b == 0) { //除数为0
None
} else {
Some(a / b)
}
}
}
注意:使用getOrElse()
方法时,当值为None时可以指定一个默认值.
match 关键字声明
,每个分支采用 case 关键字
进行声明,当需要匹配时,会从第一个 case 分支开始,如果匹配成功,那么执行对应的逻辑代码,如果匹配不成功,继续执行下一个分支进行判断。如果 所有 case 都不匹配,那么会执行 case _ 分支,类似于 Java 中 default 语句package testpackage
object TestDemo2 {
def main(args: Array[String]): Unit = {
val x: Int = 6
val y: String = x match {
case 1 => "one"
case 2 => "two"
case 3 => "three"
case _ => "other"
}
println(y) //other
println("=============================")
val a = 10
val b = 34
//用模式匹配实现简单的二元运算
def matchDualOp(op: Char): Int = op match {
case '+' => a + b
case '-' => a - b
case '*' => a * b
case '/' => a / b
case '%' => a % b
case _ => -1
}
println(matchDualOp('+')) //44
}
}
抛出 MatchError 异常
不用 break 语句
,自动中断 case=>
等价于 java swtich 的:
作为一个整体执行
,可以使用{ } 括起来,也可以不括.case语句中添加if条件判断
, 这样可以让我们的代码更简洁, 更优雅.package testpackage
object TestDemo2 {
def main(args: Array[String]): Unit = {
//求一个整数的绝对值
def abs(num:Int)=num match {
case i:Int if i>=0 => i //定义一个变量i来接收值
case j if j<0 => -j
case _ => "type illegal"
}
println(abs(10))
println(abs(-66))
}
}
package testpackage
object TestDemo2 {
def main(args: Array[String]): Unit = {
// 1.匹配常量
def describeConst(x: Any): String = x match {
case 1 => "Int one"
case "hello" => "String hello"
case true => "Boolean true"
case '+' => "Char +"
//case _ => "Not"
case a => "Not" //也可以用一个变量来接收当前的值
}
println(describeConst("hello"))
println(describeConst(1))
println(describeConst(99))
}
}
package testpackage
object TestDemo2 {
def main(args: Array[String]): Unit = {
//2.匹配类型
def describeType(x: Any): String = x match {
case i: Int => "Int " + i
case s: String => "String " + s
case list: List[String] => "List[String] " + list
case array: Array[Int] => "Array[Int] " + array.mkString(",")
case a => "Something else: " + a
}
println(describeType(100)) //Int 100
println(describeType("hello")) //String hello
//List存在泛型擦除(它只能判断到当前集合类型是List类型,里边的泛型是直接被擦掉的)
println(describeType(List(11,22,33,44,55))) //List[String] List(11, 22, 33, 44, 55)
println(describeType(List("java","scala"))) //List[String] List(java, scala)
//Array不存在泛型擦除
println(describeType(Array("hello","java"))) //Something else: [Ljava.lang.String;@75bd9247
}
}
package testpackage
object TestDemo2 {
def main(args: Array[String]): Unit = {
//3.匹配数组
for (arr <- List(
Array(0),
Array(1, 0),
Array(4,5),
Array(0, 1, 0),
Array(1, 1, 0),
Array(2, 3, 7, 15),
Array("hello", 20, 30)
)) {
val result:String = arr match {
case Array(0) => "0"
case Array(1, 0) => "Array(1,0)"
case Array(x, y) => "Array: " + x + "," + y //匹配两元素数组
case Array(0, _*) => "以0开头的数组"
case Array(x, 1, z) => "中间为1的三元素数组"
case _ => "something else"
}
println(result)
}
}
}
输出为:
0
Array(1,0)
Array: 4,5
以0开头的数组
中间为1的三元素数组
something else
something else
package testpackage
object TestDemo2 {
def main(args: Array[String]): Unit = {
// 4.匹配列表
for (list <- List(
List(0),
List(1, 0),
List(0, 0, 0),
List(1, 1, 0),
List(88),
List("hello")
)) {
val result: String = list match {
case List(0) => "0"
case List(x, y) => "List(x,y): " + x + "," + y
// case x :: y :: Nil => "List(x,y): " + x + "," + y // 匹配的是有两个元素的 List(x,y)
case List(0, _*) => "List(0,...)" //匹配的是以0开头后面有任意元素的List
case List(a) => "List(a): " + a
case _ => "something else"
}
println(result)
}
println("=============================================")
val list = List(1, 2, 3, 4, 6, 9, 7, 12)
list match {
case first :: second :: rest => println(s"first:$first,second:$second,rest:$rest")
case _ => println("something else")
}
val list2 = List(7, 12)
list2 match {
case first :: second :: rest => println(s"first:$first,second:$second,rest:$rest")
case _ => println("something else")
}
val list3 = List(12)
list3 match {
case first :: second :: rest => println(s"first:$first,second:$second,rest:$rest")
case _ => println("something else")
}
}
}
输出为:
0
List(x,y): 1,0
List(0,...)
something else
List(a): 88
List(a): hello
=============================================
first:1,second:2,rest:List(3, 4, 6, 9, 7, 12)
first:7,second:12,rest:List()
something else
list1 match {
case 0 :: Nil => println("匹配: 只有一个0元素的列表")
case 0 :: tail => println("匹配: 0开头, 后边元素无所谓(0到多个)的列表")
case x :: y :: Nil => println(s"匹配: 只有两个元素的列表, 元素为: ${x}, ${y}")
case _ => println("未匹配")
}
如果在case校验的时候, 变量没有被使用, 则可以用_
替代.
package testpackage
object TestDemo2 {
def main(args: Array[String]): Unit = {
// 5.匹配元组
for (tuple <- List(
(0, 1),
(0, 0),
(0, 1, 0),
(0, 1, 1),
(1, 23, 56),
("hello", true, 0.5)
)) {
val result: String = tuple match {
case (a, b) => "" + a + "," + b
case (0, _) => "(0,_)" // 表示匹配 0 开头的二元组
case (a, 1, _) => "(a,1,_) " + "a=" + a
case _ => "something else"
}
println(result)
}
println("===============================")
//在变量声明时匹配
val (x, y): (Int, String) = (10, "hello")
println(s"x:$x,y:$y") //x:10,y:hello
val List(first, second, _*) = List(23, 15, 9, 78)
println(s"first=$first,second=$second") //first=23,second=15
val fir :: sec :: rest = List(23, 15, 9, 78)
println(s"fir=$fir,sec=$sec,rest=$rest") //fir=23,sec=15,rest=List(9, 78)
println("==========================================")
// for推导式中进行模式匹配
val list: List[(String, Int)] = List(("a", 12), ("b", 45), ("c", 27))
//原本的遍历方式
for (elem <- list) {
println(elem._1 + " " + elem._2)
}
//将list的元素直接定义为元组,对变量赋值
for ((word, count) <- list) {
println(word + ":" + count)
}
//可以不考虑某个位置的变量,只遍历key或者value
for ((word, _) <- list) {
print(word+" ") //a b c
}
println()
//可以指定某个位置的值必须是多少
for (("a", count) <- list) {
println(count) //12
}
}
}
输出为:
0,1
0,0
(a,1,_) a=0
(a,1,_) a=0
something else
something else
===============================
x:10,y:hello
first=23,second=15
fir=23,sec=15,rest=List(9, 78)
==========================================
a 12
b 45
c 27
a:12
b:45
c:27
a b c
12
在定义变量时,可以使用模式匹配快速获取数据. 例如: 快速从数组,列表中获取数据
.
object TestDemo2 {
def main(args: Array[String]): Unit = {
//1. 生成包含0-10数字的数组,使用模式匹配分别获取第二个、第三个、第四个元素
//1.1 生成包含0-10数字的数组
val arr = (0 to 10).toArray
//1.2 使用模式匹配分别获取第二个、第三个、第四个元素
val Array(_, x, y, z, _*) = arr
//1.3 打印结果.
println(x, y, z)
println("-" * 15)
//2. 生成包含0-10数字的列表,使用模式匹配分别获取第一个、第二个元素
//2.1 生成包含0-10数字的列表,
val list = (0 to 10).toList
//2.2 使用模式匹配分别获取第一个、第二个元素
//思路一: List() 实现
val List(a, b, _*) = list
//思路二: ::, tail 实现.
val c :: d :: tail = list
//2.3 打印结果.
println(a, b)
println(c, d)
}
}
Scala中还可以使用模式匹配来匹配for表达式,从而实现快速获取指定数据, 让我们的代码看起来更简洁, 更优雅.
object TestDemo2 {
def main(args: Array[String]): Unit = {
//1. 定义变量记录学生的姓名和年龄.
val map1 = Map("张三" -> 23, "李四" -> 24, "王五" -> 23, "赵六" -> 26)
//2. 获取所有年龄为23的学生信息.
//2.1 格式一: 通过if语句实现
for((k,v) <- map1 if v == 23) println(s"${k} = ${v}")
//2.2 格式二: 通过固定值实现.
for((k, 23) <- map1) println(k + " = 23")
}
}
package testpackage
object TestDemo2 {
def main(args: Array[String]): Unit = {
val student =Student("alice", 20)
//针对对象实例的内容进行匹配
val result = student match {
case Student("alice", 18) => "alice,18"
case _ => "else"
}
println(result) //else
}
}
//定义类
class Student(val name: String, val age: Int)
//定义伴生对象
object Student {
def apply(name: String, age: Int): Student = new Student(name, age)
//必须实现一个unapply方法,用来对 对象属性进行拆解
def unapply(student: Student): Option[(String, Int)] = {
if (student == null) {
None
} else {
Some(student.name, student.age)
}
}
}
说明:
返回 Some ,且所有属性均一致,才算匹配成功
, 属性不一致,或返回 None ,则匹配失败
。unapply (obj:Obj): Option[ T ]
unapply (obj:Obj): Option[ (T1,T2,T3…) ]
unapplySeq (obj:Obj): Option[ Seq[T] ]
使用样例类:
package testpackage
object TestDemo2 {
def main(args: Array[String]): Unit = {
val student =Student("alice", 20)
//针对对象实例的内容进行匹配
val result = student match {
case Student("alice", 20) => "alice,20"
case _ => "else"
}
println(result) //alice,20
}
}
case class Student(name: String,age: Int)
apply,unapply,toString,equals,hashCode和copy
.case class
,样例类是种特殊的类,实现了类构造参数的getter
方法(构造参数默认被声明为val),当构造参数被声明为var
类型的,它将帮你实现setter
和getter
方法.toString,equals,copy和hashCode
等方法.可以new
,也可以不用new
.case class 样例类名([var/val] 成员变量名1:类型1,成员变量名2:类型2,成员变量名3:类型3){ }
(1)如果不写,则变量的默认修饰符是val,即:val是可以省略不写的
(2)如果要实现某个成员变量值可以被修改,则需手动添加var来修饰此变量.
样例类的默认方法:
当我们定义一个样例类后,编译器会自动帮我们生成一些方法,常用的如下:
val p=Person()
println(p)
打印的是对象p的各个属性值,而不是它的地址值.==
来比较两个样例类对象的所有成员变量值是否相等.p1==p2
比较的是两个对象的各个属性值是否相等,而不是比较地址值.val p1=new Person("张三",20)
val p2=new Person("张三",20)
println(p1.hashCode()==p2.hashCode()) //true
val p1=new Person("张三",20)
val p2=p1.copy(age=23)
println(p1) //张三,20
println(p2) //张三,23
package testpackage
object TestDemo4 {
def main(args: Array[String]): Unit = {
val person1 = new Person("张三", 12)
val person2 = Person("李四", 20)
val person3 = Person("王五", 32)
val personList: List[Person] = List(person1, person2, person3)
personList.foreach(person => {
person match {
case Person("张三", 12) => println("张三")
case Person("李四", 32) => println("李四")
case Person("王五", 32) => println("王五")
case _ => println("no match")
}
})
}
}
case class Person(name: String, age: Int)
结果为:
张三
no match
王五
偏函数也是函数的一种,通过偏函数我们可以方便的对输入参数做更精确的检查,例如该偏函数的输入类型是List[Int],而我们需要的是第一个元素是0的集合,这就是通过模式匹配实现的.
val second: PartialFunction[List[Int]],Option[Int]]={
case x::y::_ =>Some(y) //case语句
}
偏函数名second
偏函数类型PartialFunction[List[Int]],Option[Int]]
参数类型List[Int]
返回值类型Option[Int]]
注:该偏函数的功能是返回输入的List集合的第二个元素
val 对象名 = { //这对大括号及其内部的一组case语句, 就组成了一个偏函数.
case 值1 => 表达式1
case 值2 => 表达式2
case 值3 => 表达式3
...
}
object TestDemo2 {
def main(args: Array[String]): Unit = {
//1. 定义一个偏函数, 根据指定格式返回
val pf: PartialFunction[Int, String] = {
case 1 => "一"
case 2 => "二"
case 3 => "三"
case _ => "其他"
}
// 2. 调用方法
println(pf(1))
println(pf(2))
println(pf(3))
println(pf(4))
}
}
需求:结合map函数使用
object TestDemo2 {
def main(args: Array[String]): Unit = {
//1. 定义一个列表,包含1-10的数字
val list1 = (1 to 10).toList
// 核心: 通过偏函数结合map使用, 来进行模式匹配
val list2 = list1.map {
// 2 请将1-3的数字都转换为[1-3]
case x if x >= 1 && x <= 3 => "[1-3]"
//3 请将4-8的数字都转换为[4-8]
case x if x >= 4 && x <= 8 => "[4-8]"
// 4 将其他的数字转换为(8-*]
case _ => "(8-*]"
}
// 5. 打印结果.
println(list2)
}
}
package testpackage
object TestDemo4 {
def main(args: Array[String]): Unit = {
val list: List[(String, Int)] = List(("a", 12), ("b", 35), ("c", 27), ("a", 13))
//1.map转换,实现key不变,value*2
val newList: List[(String, Int)] = list.map(tuple => (tuple._1, tuple._2 * 2))
//2.用模式匹配对元组元素赋值,实现功能
val newList2: List[(String, Int)] = list.map(tuple => {
tuple match {
case (word, count) => (word, count * 2)
}
})
//3.省略lambda表达式的写法,进行简化 偏函数
val newList3: List[(String, Int)] = list.map {
case (word, count) => (word, count * 2)
}
//偏函数的应用,求绝对值
//对输入数据分为不同的情形:正,负,0
val positiveAbs: PartialFunction[Int, Int] = {
case x if x > 0 => x
}
val negativeAbs: PartialFunction[Int, Int] = {
case x if x < 0 => -x
}
val zeroAbs: PartialFunction[Int, Int] = {
case 0 => 0
}
//偏函数(部分函数)orElse偏函数(部分函数)orElse偏函数=一个完整的函数
def abs(x:Int):Int=(positiveAbs orElse negativeAbs orElse zeroAbs) (x)
println(abs(-97)) //97
}
}
在Scala中, 如果想将对象传输到其他虚拟机, 或者临时存储, 就可以通过序列化和反序列化
来实现了.
注意: 一个类的对象要想实现序列化和反序列化操作, 则该类必须继承Serializable特质
.
需求:
package testpackage
import java.io.{FileInputStream, FileOutputStream, ObjectInputStream, ObjectOutputStream}
object TestDemo2 {
def main(args: Array[String]): Unit = {
//1.演示序列化操作,即将对象写入到文件中
//创建Person类型的对象p
val p: Person1 = Person1("张三", 20)
//创建序列化流,用来将对象写入到文件中
val oos: ObjectOutputStream = new ObjectOutputStream(new FileOutputStream("./data/4.txt"))
//调用writeObject()方法,将对象写入到文件中
oos.writeObject(p)
//关闭序列化流
oos.close()
//2.演示反序列化操作,即:从文件中直接读取对象
//创建反序列化流,关联数据源文件
val ois = new ObjectInputStream(new FileInputStream("./data/4.txt"))
//调用readObject()方法,从数据源文件中读取指定的对象
//我们获取到的对象是AnyRef类型,所以需要转换成Person类型
val person: Person1 = ois.readObject().asInstanceOf[Person1]
println(person.name + " " + person.age)
}
}
//样例类
case class Person1(var name: String, var age: Int)
概述 :
所谓的正则表达式指的是正确的, 符合特定规则的式子
, 它是一门独立的语言, 并且能被兼容到绝大多数的编程语言中.
在scala中, 可以很方便地使用正则表达式来匹配数据。具体如下:
r方法
即可.格式:
val 正则对象名 = """具体的正则表达式""".r
注意: 使用findAllMatchIn方法可以获取到所有正则匹配到的数据(字符串).
object TestDemo2 {
def main(args: Array[String]): Unit = {
//需求: 定义一个正则表达式,来匹配邮箱是否合法
// 1. 定义一个字符串, 表示邮箱
val email = "[email protected]"
// 2. 定义一个正则表达式, 用来校验邮箱.
/*
. 表示任意字符
+ 数量词, 表示前边的字符出现至少1次, 至多无所谓.
@ 表示必须是@符号, 无特殊含义.
\. 因为.在正则中有特殊的含义, 所以要转义一下, 使它变成普通的.
*/
//创建一个正则对象
val regex: Regex = """.+@.+\..+""".r
//3. 打印结果
if (regex.findAllMatchIn(email).size != 0) {
// 合法邮箱
println(s"${email} 是一个合法的邮箱!")
} else {
println(s"${email} 是一个非法的邮箱!")
}
}
}
示例: 获取邮箱运营商
需求:
"[email protected]", "[email protected]", "[email protected]", "123afadff.com"
object TestDemo2 {
def main(args: Array[String]): Unit = {
//1. 定义列表, 记录邮箱.
val emlList = List("[email protected]", "[email protected]", "[email protected]", "123afadff.com")
// 2. 定义正则表达式.
val regex = """.+@(.+)\..+""".r
// 3. 根据 模式匹配 匹配出所有合法的邮箱及其对应的运营商.
val result = emlList.map {
// email就是emlList这个列表中的每一个元素.
// company表示: 正则表达式中你用()括起来的内容, 也就是分组的数据.
// @regex()是固定写法
case email@regex(company) => email -> s"${company}" //元组
case email => email -> "未匹配"
}
// 4. 打印结果
println(result) //List(([email protected],qq), ([email protected],gmail), ([email protected],163), (123afadff.com,未匹配))
}
}
(1) Java异常处理
package testpackage;
public class JavaException {
public static void main(String[] args) {
try {
int i = 0;
int b = 10;
int c = b / i;
// 执行代码时,会抛出 ArithmeticException 异常
} catch (ArithmeticException e) {
e.printStackTrace();
} catch (Exception e) {
//catch时,需要将范围小的写到前面
e.printStackTrace();
} finally {
// 最终要执行的代码
System.out.println("java finally");
}
System.out.println("程序继续执行~~"); }
}
try—catch-catch...—finally
的方式来处理异常(2)Scala异常处理
package testpackage
object ScalaException {
def main(args: Array[String]): Unit = {
try {
var res = 10 / 0
} catch {
case ex: ArithmeticException => {
println("算术异常=" + ex.getMessage)
}
case ex: Exception =>{
//对异常进行处理
println("异常信息=" + ex.getMessage)
}
} finally {
println("finaly 的代码...")
}
println("程序继续....")
}
}
输出为:
算术异常=/ by zero
finaly 的代码...
程序继续....
(3)Scala 异常处理小结
Scala 没有“checked(编译期)”异常
,即 Scala 没有编译异常
这 个概念,异常都是在运行的时候捕获处理。异常都是 Throwable 的子类型
。throw 表达式是有类型的,就是Nothing
,因为 Nothing 是所有类型的子类型,所以 throw 表达式可以用在需要类型的地方. def main(args: Array[String]): Unit = {
val res: Nothing = test()
println(res)
}
def test(): Nothing = {
throw new Exception("异常")
}
在 catch 子句中, 越具体的异常越要靠前,越普遍的异常越靠后
,如果把越普遍的异常写在前,把具体的异常写在后,在 scala 中也 不会报错(不报错,但是不推荐),但这样是非常不好的编程风格。用于对象的清理工作
,这点 和 Java 一样。声明异常
。可以使用方法定义声明异常。 它向调用者函数提供了此方法可能 引发此异常的信息
。 它有助于调用函数处理并将该代码包含在 try-catch 块中,以避免程序异常终止。在 scala 中, 可以使用 throws 注释来声明异常 def main(args: Array[String]): Unit = {
f11()
}
@throws(classOf[NumberFormatException])//等同于 NumberFormatException.class
def f11() = {
"abc".toInt
}
当编译器第一次编译失败的时候,会在当前的环境中查找能让代码编译通过的方法,用于将类型进行转换,实现二次编译.
implicit 关键字
声明的带有单个参数
的函数。该函数是被自动调用的,将值从一种类型转换 为另一种类型
函数名可以是任意的
,隐式转换与函数名称无关,只与函数签名(函数参数类型和返回值类型
)有关。def main(args: Array[String]): Unit = {
//在当前环境中,不能存在满足条件的多个隐式函数
// implicit def fun1(d: Double) = d.toInt
implicit def fun2(d: Double) = d.toInt
//(x) 在转换时,识别出有两个方法(fun1和fun2)可以被使用,就不能确定调用哪一个,所以出错
val num:Int=3.5
println(num) //3
}
package testpackage
object TestImplicitFunction {
//使用implicit关键字声明的函数称之为隐式函数
implicit def convert(arg: Int): MyRichInt = {
new MyRichInt(arg)
}
def main(args: Array[String]): Unit = {
//val richInt = new MyRichInt(12)
// println(richInt.myMax(66)) //66
/* 当想调用对象功能(如:myMax())时,如果编译错误,那么编译器会尝试在当前作用域范围内查找能调用对应功能的转换规则,
这个调用过程是由编译器完成的,所以称之为隐式转换,也称之为自动转换.
*/
println(2.myMax(6))
}
}
class MyRichInt(val self: Int) {
def myMax(i: Int): Int = {
if (self < i) i else self
}
def myMin(i: Int): Int = {
if (self < i) self else i
}
}
implicit关键字修饰的变量
.package testpackage
object TestImplicitValue {
//隐式变量(隐式值)
implicit val str: String = "hello scala"
/*
def hello()(implicit arg:String="very good"):Unit={
println(arg)
}
*/
/* def hello(implicit arg:String="very good"):Unit={
println(arg)
}*/
//简便写法
def hello(): Unit = {
//调用implicitly方法 想要拿到一个String类型的隐式参数
println(implicitly[String])
}
def main(args: Array[String]): Unit = {
//因为上面省略了(),所以不能加()
hello
}
}
构造参数有且只能有1个
顶级的
package testpackage
object TestImplicitClass {
implicit class MyRichInt(arg: Int) {
def myMax(i: Int): Int = {
if (arg < i) i else arg
}
def myMin(i: Int): Int = {
if (arg < i) arg else i
}
}
def main(args: Array[String]): Unit = {
println(1.myMax(3)) //3
}
}
即编译器是如何查找到缺失信息的,解析具有以下两种规则:
当前代码作用域下查找隐式实体
(隐式方法、隐式类、隐式对象)。(一般是这种情况)与该类 型相关联的全部伴生对象
以及该类型所在包的包对象
,一个隐式实体的类型 T 它的查找范围如下(第二种情况范围广且复杂在使用时,应当尽 量避免出现
):class MyList[+T]{ //协变 }
class MyList[-T]{ //逆变 }
class MyList[T] //不变
子类
,则MyList[Son]也作为MyList[Father]的子类
.子类
,则MyList[Son]也作为MyList[Father]的父类
.都无父子关系
.package testpackage
object TestDemo4 {
def main(args: Array[String]): Unit = {
//1. 协变
val childList: MyCollection1[Parent] = new MyCollection1[Child]
//2. 逆变
val MyList: MyCollection2[Child] = new MyCollection2[Parent]
}
}
//定义继承关系
class Parent {}
class Child extends Parent {}
//定义带泛型的集合类型
class MyCollection1[+E] {}
class MyCollection2[-E] {}
class PersonList[T<:Person]{ //泛型上限 }
class PersonList[T>:Person]{ //泛型下限 }
package testpackage
object TestDemo4 {
def main(args: Array[String]): Unit = {
test(new Child) //testpackage.Child
test[Child](new SubChild) //testpackage.SubChild
}
//泛型通配符之上限
def test[A <: Child](a: A): Unit = {
println(a.getClass.getName)
}
//泛型之通配符之下限
/* def test[A >: Child](a: A): Unit = {
println(a)
}*/
/* //泛型之通配符之下限 形式扩展
def test[A >: Child](a: A): Unit = {
println(a.getClass.getName)
}*/
}
//定义继承关系
class Parent {}
class Child extends Parent {}
class SubChild extends Child {}
def fun[A:B](a:A)=println(a)
等同于def fun[A](a:A)(implicit arg:B[A])=println(a)
implicitly[Ordering[A]]
获取隐式变量,如果此时无法查找到对应类型的隐式变量,会发生错误. implicit val x:Int=10
val y=implicitly[Int]
val z=implicitly[Double] //报错
println(y) //10