Scala笔记
2023-06-07 13:43:52 0 举报
AI智能生成
Scala笔记
作者其他创作
大纲/内容
Scala概述
Scala入门第一步
Scala环境的搭建(基于Mac系统)
解压文件
vim /etc/profile
配置路径
source /etc/profile
Idea下Scala插件的加载
对应版本下载plugins
install plugins from disk
重启,创建maven项目
add framework support
Scala是什么
Scala是一门以java虚拟机(JVM)为目标运行环境并将面向对象和函数式编程的最佳特性结合在一起的静态类型编程语言
补充:
静态语言与动态语言
动态语言
运行时代码可以根据某些条件改变自身结构
静态语言
运行时结构不可变的语言就是静态语言
静态类型语言与动态类型语言
动态类型语言
是指在运行期间才去做数据类型检查的语言,说的是数据类型
静态类型语言
在编译期间(或运行之前)确定的,编写代码的时候要明确确定变量的数据类型
解释型语言与编译型语言
编译型语言
需通过编译器(compiler)将源代码编译成机器码,之后才能执行的语言
解释型语言
解释性语言的程序不需要编译,相比编译型语言省了道工序,解释性语言在运行程序的时候才逐行翻译
Scala语言特点
Scala 是一门多范式 (multi-paradigm) 的编程语言,Scala支持面向对象和函数式编程
Scala源代码(.scala)会被编译成Java字节码(.class),然后运行于JVM之上,并可以调用现有的Java类库,实现两种语言的无缝对接。
简洁高效
基于Java设计思想,融合函数式编程语言,注意Java与Scala的相同与不同
Scala执行代码的基本结构与Java的对比
Scala的执行代码基本结构
代码示例
object ScalaTest {
def main(args : Array[String]) : Unit = {
println("Hello World")
}
}
与Java的对比
使用object关键字声明
如果使用Object声明类,可以编译出两个类,一个是当前类,另外一个是当前类的辅助类(类名$),执行时,辅助类可以直接构建对象使用(伴生对象)
主要目的是模拟静态语法
声明main方法
使用def关键字(defined)声明
不需要使用public关键字声明
Scala中没有public关键字,默认public访问权限
不需要使用static关键字声明
Scala完全面向对象,所以没有静态语法,因而没有static关键字
静态是基于类的,而非对象,面向对象,因而没有静态语法
不需要使用void关键字声明返回值类型
Scala无void关键字,因为void也并非面向对象语法
方法都有参数列表,但是参数的声明方式不同
java :参数类型 参数名
scala : 参数名 :参数类型
scala中的Unit类型代表无返回值 void => Unit
java : 返回值类型 方法名
scala : 方法名 () : 返回值类型
Scala变量
变量与数据类型
变量定义
基本语法
var | val 变量名[: 变量类型]
注意事项
类型推断:声明时可省略类型
强数据类型:类型确定后不可更改
声明变量时必须有初始值
var修饰可更改,val修饰不可更改
val指向的是引用,只要更改它的引用项就可以更改
但对象的状态(值)却是可以改变的。(比如: 自定义对象、数组、集合等等)
Scala数据类型
Any
AnyVal
浮点型
Double
64位,双精度浮点数
Float
32位,单精度浮点数
整型
Long
64位
Int
32位,-21E~21E
Short
16位,-3W2~3W2
Byte
8位,-128到127
Boolean
Char
StringOps
Unit
表示无值,和其他语言中的void等同,只有一个实例()
AnyRef
Scala collections
all java classes
Other Scala classes
备注:Null为所以引用对象的子类,只有一个实例null
备注
Nothing是所有类型的子类
图示
分支主题
值类型转换
规则
小转大,自动转
大转小,需强转
自动类型转换(implicit conversion)
细节
多种类型混合运算时,自动将所有数据转为最大的
(byte, short) 和 char之间不会相互自动转换
强制类型转换
用法
.toXxx
标识符的命名规范
标识符命名规则
标识符命名规范
Scala运算符
算术运算符
/号整数除与小数除有区别
整数除只保留整数部分
Scala中没有++、--运算符,通过+=、-=来实现同样效果
比较运算符
符号
==
!=
<
>
<=
>=
结果
True Or False
逻辑运算符
&&
&
||
|
赋值运算符
=
+=
-=
*=
/=
%=
<<=
>>=
&=
^=
|=
位运算符
&
|
^
~
<<
>>
>>>
Scala程序流程控制
顺序控制
分支控制
类型
if-else
单分支
双分支
多分支
类Switch分支
match-case
基础知识
代码实例
val oper = '#'
val n1 = 20
val n2 = 10
var res = 0
oper match {
case '+' => res = n1 + n2
case '-' => res = n1 - n2
case '*' => res = n1 * n2
case '/' => res = n1 / n2
case _ => println("oper error")
}
println("res=" + res)
注意事项
都不匹配,执行case _,类似于default
都不匹配,有没有case _,抛出MatchError
不用break语句,自动中断case
case _ 在前则先执行case _,然后跳出
守卫
概念介绍
如果想要表达匹配某个范围的数据,就需要在模式匹配中增加条件守卫
代码实例
for (ch <- "+-3!") {
var sign = 0
var digit = 0
ch match {
case '+' => sign = 1
case '-' => sign = -1
case _ if ch.toString.equals("3") => digit = 3
case _ => sign = 2
}
println(ch + " " + sign + " " + digit)
}
具体匹配
模式中的变量
case后接变量,则将表达式值赋给变量
类型匹配
代码实例
val result = obj match {
case a : Int => a
case b : Map[String, Int] => "对象是一个字符串-数字的Map集合"
case c : Map[Int, String] => "对象是一个数字-字符串的Map集合"
case d : Array[String] => "对象是一个字符串数组"
case e : Array[Int] => "对象是一个数字数组"
case f : BigInt => Int.MaxValue
case _ => "啥也不是"
}
println(result)
对象匹配
样例类
样例类默认很多方法,供直接使用
样例类仍然是类
样例类用case关键字进行声明。
样例类是为模式匹配(对象)而优化的类
构造器中的每一个参数都成为val——除非它被显式地声明为var
在样例类对应的伴生对象中提供apply方法让你不用new关键字就能构造出相应的对象
提供unapply方法让模式匹配可以工作
将自动生成toString、equals、hashCode和copy方法(有点类似模板类,直接给生成,供程序员使用)
除上述外,样例类和其他类完全一样。你可以添加方法和字段,扩展它们
代码实例
abstract class Amount
case class Dollar(value: Double) extends Amount
case class Currency(value: Double, unit: String) extends Amount
case object NoAmount extends Amount
case语句的中置表达式
代码实例
var list = List(1, 3, 5, 9)
list match {
case first :: second :: rest => println(first + “**” + second + “**” + rest)
case _ => println("匹配不到...")
}
密封类
如果想让case类的所有子类都必须在申明该类的相同的源文件中定义,可以将样例类的通用超类声明为sealed,这个超类称之为密封类。
代码实例
abstract sealed class Amount
case class Dollar(value : Double) extends Amount
case class Currency(value:Double,unit : String) extends Amount
case object Nothing extends Amount
嵌套分支
一般嵌套不超过3层
Scala注意事项
Scala中任意表达式都是有返回值的,也就意味着if else表达式其实是有返回结果的,具体返回结果的值取决于满足条件的代码体的最后一行内容.
Scala中没有三元运算符
val result = if (flg) 1 else 0
循环控制
for
基本循环
for(i <- 1 to 3)
for(i <- 1 until 3)
循环守卫
for(i <- 1 to 3 if i != 2)
true进入循环内部
false跳过
循环返回值
val result = for(i <- 1 to 10) yield i * 2
println(result)
多行逻辑小括号变大括号
for {
i <- 1 to 5
j = i - 1
} {
}
循环步长
方法1
for ( i <- 1.to(10,2) ) {
println("i = " + i)
}
方法2
for ( i <- Range(1, 10, 2) ) {
println(i)
}
while
while循环中的Break操作
var i = 0
breakable{
while(i < 10){
if(i == 5) {
break
}
println("i=" + i)
i = i +1
}
}
函数式编程思想
函数概述
函数的声明:方法即函数,类中函数称方法,方法有重载和重写,函数没有
def 函数名(参数列表) : 函数返回值类型 = {
函数体
}
Scala语法松散,任何语法结构都可以嵌套,方法中可以声明函数,函数中可以声明函数,但方法中不能声明方法
Scala中的可变参数与带名参数
可变参数在类型后面加*,可变参数应该在参数列表的最后声明
当一个函数中有多个传入参数,且有默认值时,建议使用带名参数,带名指定赋值参数
Scala语法规则
声明函数后,可以将函数体最后一行作为返回值,所以return关键字可以省略
def f2(name : String) : String = {
"Name :" + name
}
如果函数可以自动推断返回值类型,那么返回值类型可以省略
def f3(name : String) = {
"Name :" + name
}
如果函数逻辑只有一行代码,那么花括号可以省略
def f4(name : String) = "Name" + name
如果参数列表没有参数,那么参数列表的括号可以省略
def f5 = "abc"
参数列表
如果函数的参数列表已声明,那么调用时可以使用小括号,也可以不使用
如果函数的参数列表未声明,那么调用时不能使用小括号
如果函数体中有明确的return操作,必须声明返回值类型
def f6() : Int = {
return 2
}
如果函数声明返回值类型为Unit,那么函数体中的return不起作用
def f7() : Unit = {
return 2
}
如果Unit类型也想省略,可以同时将等号省略
def f8(){
return 3
}
如果只关心逻辑,而不关心方法名称,那么函数名称可以省略,def关键字也可省略
()->{println("xxxxxxxx")}
匿名函数
val f9 = (i : Int)=>{println("xxxxx")}
函数柯里化和函数闭包
函数柯里化
柯里化指的是将原来接受多个参数的函数变成新的接受一个参数的函数的过程,
新函数的参数接受原来的第二个参数为唯一参数
如果有n个参数, 就是把这个函数分解成n个新函数的过程
函数闭包
如果函数中使用外部变量,为了防止变量数据丢失,将变量包含到函数的内部,形成闭合的效果,形成闭包,改变变量的生命周期
闭包就是一个函数和与其相关的引用环境(变量/值)组合的一个整体(实体)。
代码实例
def minusxy(x: Int) = (y: Int) => x – y
//minusxy 他会返回一个匿名函数 (y: Int) => x – y
//匿名函数,他使用了一个外部的变量 x
//f函数就是闭包.
val f = minusxy(20)
println("f(1)=" + f(1)) //
println("f(2)=" + f(2)) //
递归
递归的注意事项
函数自己调用自己
函数应该有跳出递归的逻辑
函数调用自身的深度不要太深
递归函数在调用时,需要传递的参数有规律
Scala中,递归函数不能省略返回值类型
惰性函数
lazy不可修饰var型变量
异常
Scala中的异常处理与Java类似
try {
val r = 10 / 0
} catch {
case ex: ArithmeticException=> println(“捕获了除数为零的算术异常")
case ex: Exception => println("捕获了异常")
} finally {
// 最终要执行的代码
println("scala finally...")
}
case类似于switch case的写法
偏函数
处理全部数据,选取自己想要的数据,其他数据抛弃
当使用偏函数时,会遍历集合的所有元素,编译器执行流程时先执行isDefinedAt()如果为true ,就会执行 apply, 构建一个新的Int 对象返回
执行isDefinedAt() 为false 就过滤掉这个元素,即不构建新的Int对象.
代码实例
完整版
val list = List(1, 2, 3, 4, "abc")
//说明
val addOne3= new PartialFunction[Any, Int] {
def isDefinedAt(any: Any) = {
if (any.isInstanceOf[Int]) true
else false
}
def apply(any: Any) = any.asInstanceOf[Int] + 1
}
简化版
val list3 = List(1, 2, 3, 4,"ABC").collect{ case i: Int => i + 1 }
println(list3)
控制抽象
控制抽象概念
参数是函数
函数参数没有输入值也没有返回值
代码实例
def myRunInThread(f1: => Unit): Unit = { 11; new Thread {11; override def run(): Unit = {11; f1 11; }11; }.start()11;}11;myRunInThread { //说明11; println("干活咯!5秒完成...") Thread.sleep(5000)11; println("干完咯!")11;}
面向对象编程思想
课程笔记
Java面向对象编程与Scala面向对象编程对比
Java面向对象编程
package com.atguigu.xxx
import java.util.List
class Test extends Parent implements Interface {
private String fields;
public boolean login(){
return true
}
}
Scala面向对象编程
package com.atguigu.base.Object
object Scala01_Object {
def main(args: Array[String]): Unit = {
val user = new User()
println(user.name)
}
}
包(package)
与Java的对比
Java包的作用
分门别类的管理的类
区分相同名称的类
包的访问权限
Scala包的作用
package关键字可以多次声明
Scala源码和包名称没有直接的关系
package可以声明作用域
Scala将包当成一个对象
包的命名
域名的反写 + 项目名 + 模块 + 程序类型
包对象
Import
可以在任意的地方使用
可以导入指定包中的类,java.lang和scala中的类不需要
导入指定包中所有的类,在scala中使用下划线代替java中的星号
import java.util._
导入指定包中的多个类,可以使用大括号,声明在一行
import java.util.{ArrayList,HashMap}
scala中的import可以导包
import java.util
scala可以在导类时候隐藏指定的类
import sacala.collection.mutable._
import java.util.{HashMap=>_,_}
scala可以给导入的类起别名
import java.util.{HashMap=>JavaHashMap,_}
scala中包的概念是相对路径,这样可能会出现冲突,一般采用绝对路径的方式解决
new _root_.java.util.HashMap
类与对象
类
对象
属性
类的属性在编译后全部都是private,同时提供两个公共方法(set,get)
为了和java的开发规范匹配,提供注解(@BeanProperty)来生成2对set/get方法(Scala和Java的)
Scala也可以给属性默认初始化,使用_
私有属性,类内部使用,外部无法使用
private var name = "ZHANGSAN"
Scala中受保护的权限只能子类访问,同包无法访问
protected var age = 20
使用private作为访问修饰符
private后需要增加中括号,指明能访问包的名称
private[Object] var email = "xxxx@163.com"
方法
类中的方法其实就是函数,所以遵循函数的声明和调用
Scala中允许不使用new构建对象,会调用伴生对象的特殊方法来构建对象
类与对象的内存分配机制
构造器
主构造器(一个) 和 辅助构造器(多个),构造器也是方法,辅助构造函数一定要直接或间接地调用主构造函数
基本语法
class 类名(形参列表) { // 主构造器
// 类体
def this(形参列表) { // 辅助构造器
}
def this(形参列表) { //辅助构造器可以有多个...
}
}
面向对象三大原则
封装
定义
封装(encapsulation)就是把抽象出的数据/属性和对数据的操作/方法封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(成员方法),才能对数据进行操作。
细节
声明属性时,本身就自动提供了对应setter/getter方法
如果属性声明为private的,那么自动生成的setter/getter方法也是private的
如果属性省略访问权限修饰符,那么自动生成的setter/getter方法是public的
继承
定义
实例
isInstanceOf
asInstanceOf
classOf
子类与父类
类有一个主构器和任意数量的辅助构造器,而每个辅助构造器都必须先调用主构造器(也可以是间接调用)
只有主构造器可以调用父类的构造器。辅助构造器不能直接调用父类的构造器。在Scala的构造器中,你不能调用super(params)
动态绑定机制
当调用对象方法的时候,该方法会和该对象的内存地址绑定
当调用对象属性时,没有动态绑定机制,哪里声明,那里使用
抽象类
类通过abstarct修饰
方法省略掉方法体
代码实例
abstract class Person() { // 抽象类
var name: String // 抽象属性, 没有初始化
var age = 20 // 普通属性
def printName // 抽象方法 没有方法体
def test() = {
println(“test…”)
}
}
说明
抽象类不能被实例化
抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法
一旦类包含了抽象方法或者抽象属性,则这个类必须声明为abstract
抽象方法不能有主体,不允许使用abstract修饰。
如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法和抽象属性,除非它自己也声明为abstract类。
抽象方法和抽象属性不能使用private、final 来修饰,因为这些关键字都是和重写/实现相违背的。
抽象类中可以有实现的方法.
子类重写抽象方法不需要override,写上也不会错(另一种叫法是实现).
匿名子类
通过包含带有定义或重写的代码块的方式创建一个匿名的子类
多态
静态属性和静态方法
Java中静态方法并不是通过对象调用的,而是通过类对象调用的,所以静态操作并不是面向对象的。
Scala中静态的概念-伴生对象,模拟类对象,这个类的所有静态内容都可以放置在它的伴生对象中声明和调用
实例
分支主题
apply方法
在伴生对象中定义apply方法,可以实现: 类名(参数) 方式来创建对象实例.
总结
Scala中伴生对象采用object关键字声明,伴生对象中声明的全是 "静态"内容,可以通过伴生对象名称直接调用。
伴生对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。
伴生对象中的属性和方法都可以通过伴生对象名直接调用访问
从语法角度来讲,所谓的伴生对象其实就是类的静态方法和静态变量的集合
从技术角度来讲,scala还是没有生成静态的内容,只不过是将伴生对象生成了一个新的类,实现属性和方法的调用。
从底层原理看,伴生对象实现静态特性是依赖于 public static final MODULE$ 实现的。
伴生对象的声明应该和伴生类的声明在同一个源码文件中(如果不在同一个文件中会运行错误!),但是如果没有伴生类,也就没有所谓的伴生对象了,所以放在哪里就无所谓了。
如果 class A 独立存在,那么A就是一个类, 如果 object A 独立存在,那么A就是一个"静态"性质的对象[即类对象], 在 object A中声明的属性和方法可以通过 A.属性 和 A.方法 来实现调用
特质(Trait)
Scala语言中,采用trait(特质,特征)来代替接口的概念
Trait的声明
trait 特质名 {
trait体
}
特质的继承使用
如果类存在父类,首先初始化父类
如果类存在特质,那么会首先初始化特质
特质初始化后,才会进行类的初始化
父类初始化完成后,类存在多个特质,从左到右依次初始化
执行特质中的功能时,从右往左
特质只会初始化一次
Trait的动态插入
动态混入是Scala特有的方式(java没有动态混入),可在不修改类声明/定义的情况下,扩展类的功能,非常的灵活,耦合性低
在构建对象时混入特质,扩展对象的功能
隐式转换和隐式参数
基本概念
隐式转换函数是以implicit关键字声明的带有单个参数的函数。这种函数将会自动应用,将值从一种类型转换为另一种类型
代码实例
implicit def f1(d: Double): Int = {11; d.toInt11;}
object Scala01 {
def main(args: Array[String]): Unit = {
val num1 : Int = 3.5
val num2 : Int = 4.6
println(num1)
println(num2)
}
}
隐式转换在丰富功能的应用
给MySQL类增加delete方法
思路:给MySQL类增加一个隐式转换,转换为另外的类,并在该类中写明增加的方法
代码实例
class MySQL{
def insert(): Unit = {
println("insert")
}
}
class DB {
def delete(): Unit = {
println("delete")
}
}
implicit def addDelete(mysql:MySQL): DB = {
new DB //
}
val mysql = new MySQL
mysql.insert()
mysql.delete()
隐式值
作为缺省参数使用
隐式类
作用
扩展功能
特点
其所带的构造参数有且只能有一个
隐式类必须被定义在“类”或“伴生对象”或“包对象”里,即隐式类不能是 顶级的(top-level objects)。
隐式类不能是case class(case class在后续介绍 样例类)
作用域内不能有与之相同名称的标识符
代码实例
def main(args: Array[String]): Unit = {
//DB1会对应生成隐式类
implicit class DB1(val m: MySQL1) {
def addSuffix(): String = { //方法
m + " scala"
}
}
val mysql1 = new MySQL1
mysql1.sayOk()
println(mysql1.addSuffix())
}
class MySQL1 {
def sayOk(): Unit = {
println("sayOk")
}
}
Scala集合
基本概述
分类
可变不可变
不可变集合:scala.collection.immutable
可变集合: scala.collection.mutable
三大类
序列Seq
集Set
映射Map
Scala默认采用不可变集合
基础集合类型
元组Tuple
访问通过_顺序号,顺序号从1开始
遍历
productIterator
数组Array
数组-定长数组(声明泛型)
声明
val arr1 = new Array[Int](10)
val arr1 = Array(1, 2)
数组-变长数组(声明泛型)
val arr2 = ArrayBuffer[Int]()
每append一次,arr在底层会重新分配空间,进行扩容,arr2的内存地址会发生变化,也就成为新的ArrayBuffer
定长变长数组转换
arr1.toBuffer //定长数组转可变数组
arr2.toArray //可变数组转定长数组
Scala数组和Java数组的转换
Scala数组转Java List
val arr = ArrayBuffer("1", "2", "3")
import scala.collection.JavaConversions.bufferAsJavaList
val javaArr = new ProcessBuilder(arr)
val arrList = javaArr.command()
Java List转Scala数组
import scala.collection.JavaConversions.asScalaBuffer
import scala.collection.mutable
// java.util.List ==> Buffer
val scalaArr: mutable.Buffer[String] = arrList
List(属于Seq)
Scala的List可以直接存放数据,就是一个object
空集合的运用
val list02 = Nil
List元素的追加
在列表的最后增加数据
var list1 = List(1, 2, 3, "abc")
val list2 = list1 :+ 4
在列表的最前面增加数据
var list1 = List(1, 2, 3, "abc")
val list2 = 4 +: list1
在列表的最后增加数据
val list5 = 4 :: 5 :: 6 :: list1 :: Nil // 从右向左的逻辑
val list7 = 4 :: 5 :: 6 :: list1 ::: Nil
::表示新建集合添加元素,:::表示将集合中每一个元素加入到空集合中
ListBuffer
ListBuffer是可变的list集合,可以添加删除元素,ListBuffer属于序列
队列Queue
基本概述
队列是一个有序列表,在底层可以用数组或是链表来实现,遵循FIFO,一般使用可变集合中的数列
创建
val q1 = new mutable.Queue[Int]
映射Map
基本概述
Scala中不可变的Map是有序的,可变的Map是无序的
集Set
基本概述
集是不重复元素的结合
创建
不可变集合
val set = Set(1, 2, 3) //不可变
可变集合
import scala.collection.mutable.Set
val mutableSet = Set(1, 2, 3) //可变
集合的方法和应用
map映射函数
实例应用1
val names = List("Alice", "Bob", "Nick")
val strings2 = names.map(_.toUpperCase)
实例应用2
val tuples = List(("Hello Scala",4),("Hello Spark",3),("Spark Scala",2))
val strings = tuples.map(t => { (t._1 + " ") * t._2})
flatmap扁平化映射
实例应用
map与flatmap的区别
定义解释
map
Return a new distributed dataset formed by passing each element of the source through a function func.
flatmap
Similar to map, but each input item can be mapped to 0 or more output items (so func should return a Seq rather than a single item).
在使用时map会将一个长度为N的RDD转换为另一个长度为N的RDD;而flatMap会将一个长度为N的RDD转换成一个N个元素的集合,然后再把这N个元素合成到一个单个RDD的结果集。
可以理解为flatmap比map多了一个flatten操作
图示
分支主题
filter过滤
代码示例
val tuples = list.filter(word => {word._1.contains("H")})
reduceleft归约
val list = List(1,20,30,4,5)
println(list.reduceLeft((x, y) => (x + y)))
println(list.reduceLeft(_ + _))
fold
fold函数将上一步返回的值作为函数的第一个参数继续传递参与运算,直到list中的所有元素被遍历。
foldLeft /:
foldRight :\
scanLeft
对某个集合的所有元素做fold操作,但是会把产生的所有中间结果放置于一个集合中保存
拉链zip
流Stream
stream是一个集合。这个集合,可以用于存放无穷多个元素,但是这无穷个元素并不会一次性生产出来,而是需要用到多大的区间,就会动态的生产,末尾元素遵循lazy规则(即:要使用结果才进行计算的) 。
模式匹配
基本概述
类似于Java中的switch语法,都不匹配执行case _分支,如果没有case _分支则抛出异常
代码实例
val oper = '#'
val n1 = 20
val n2 = 10
var res = 0
oper match {
case '+' => res = n1 + n2
case '-' => res = n1 - n2
case '*' => res = n1 * n2
case '/' => res = n1 / n2
case _ => println("oper error")
}
println("res=" + res)
守卫代码实现
for (ch <- "+-3!") {
var sign = 0
var digit = 0
ch match {
case '+' => sign = 1
case '-' => sign = -1
case _ if ch.toString.equals("3") => digit = 3
case _ => sign = 2
}
println(ch + " " + sign + " " + digit)
}
类型匹配
val result = obj match {
case a : Int => a
case b : Map[String, Int] => "对象是一个字符串-数字的Map集合"
case c : Map[Int, String] => "对象是一个数字-字符串的Map集合"
case d : Array[String] => "对象是一个字符串数组"
case e : Array[Int] => "对象是一个数字数组"
case f : BigInt => Int.MaxValue
case _ => "啥也不是"
}
println(result)
Map[String, Int] 和Map[Int, String]是两种不同的类型,其它类推。
在进行类型匹配时,编译器会预先检测是否有可能的匹配,如果没有则报错.
匹配数组
for (arr <- Array(Array(0), Array(1, 0), Array(0, 1, 0),
Array(1, 1, 0), Array(1, 1, 0, 1))) {
val result = arr match {
case Array(0) => "0"
case Array(x, y) => x + "=" + y
case Array(0, _*) => "以0开头和数组"
case _ => "什么集合都不是"
}
println("result = " + result)
}
匹配列表
for (list <- Array(List(0), List(1, 0), List(0, 0, 0), List(1, 0, 0))) {
val result = list match {
case 0 :: Nil => "0" //
case x :: y :: Nil => x + " " + y
case 0 :: tail => "0 ..."
case _ => "something else"
}
println(result)
}
匹配元组
for (pair <- Array((0, 1), (1, 0), (2, 1),(1,0,2))) {
val result = pair match {
case (0, _) => "0 ..."
case (y, 0) => y
case (a,b) => (b,a)
case _ => "other"
}
println(result)
}
对象匹配
条件
case中对象的unapply方法(对象提取器)返回Some集合则为匹配成功
返回none集合则为匹配失败
代码实例
val namesString = "Alice,Bob,Thomas"
namesString match {
case Names(first, second, third) => {
println("three people's names")
// 打印字符串
println(s"$first $second $third")
}
case _ => println("nothing matched")
}
泛型
基础
Scala用[]规定泛型
泛型的上下界
上界
[T <: A]
下界
[T >: A]
视图界定
<% 的意思是“view bounds”(视界),它比<:适用的范围更广,除了所有的子类型,还允许隐式转换类型。
上下文界定
协变
C[+T]:如果A是B的子类,那么C[A]是C[B]的子类,称为协变
逆变
C[-T]:如果A是B的子类,那么C[B]是C[A]的子类,称为逆变
不可变
C[T]:无论A和B是什么关系,C[A]和C[B]没有从属关系。称为不变.
Scala中可通过+ - 号,如tarit List[+T]表协变
0 条评论
下一页