scala

一.变量

scala的数据类型

  • Any:根类型,所有类的父类,它有两个直接子类,AnyValue,AnyRef

  • AnyRef:所有Scala中引用类(reference class)的父类

  • Null:所有引用类的子类,类似与java中的null

  • Nothing:所有类的子类,可以将Nothing类型的值返回给任意变量或者函数

    数据类型列表

变量声明

1
2
3
4
5
6
7
规则:
val/var vname:type = value
var i //error 变量声明必须初始化
var x = _ //error 需要明确具体的类型
val y = _ //error val不可变,需要确定的值
var i:int = 10
val j:Char = 'a'

var和val的区别:

var修饰的变量是可变的,val修饰的变量是不可变的,它底层其实是使用final关键字修饰

键盘输入语句

1
2
3
4
5
println("请输入你的姓名")
var name = StdIn.readLine()

println("请输入你的证件号")
var card = StdIn.readLine();

二.程序流程控制

1. if-else

与java中的if-else 差不多

2.for循环控制

Scala为for循环这一常见的控制结果提供了非常多的特性,这些特性被成为for推导式或or表达式

  • 范围数据循环方式
1
2
3
4
5
6
7
8
9
10
11
12
13
//两边都是闭合的 
for(i<-start to end){

}
//左闭右开
for(i<-start until end){

}
//对集合进行循环遍历
var list = List("hello","world","good")
for (i<-list){
println(i)
}
  • 循环守卫
1
2
3
4
 //条件为true的进入循环体内部 为false的则不进入
for (i<-1 to 10 if i%2!=0 ){
println(i)
}
  • 引入变量
1
2
3
 for (i<-1 to 10;j = 10-i ){
println(i+":"+j)
}
  • 嵌套循环
1
2
3
4
 //嵌套循环
for (i<-1 to 3;j<-1 to 3){
println("xxx")
}
  • for 推倒 yield将结果收集
1
2
3
//y 是一个Vector集合 Vector(1, 2, 3)
val y = for (i<-1 to 3) yield i
println(y)
  • 使用花括号
1
2
3
4
5
6
7
for {
i<-1 to 3
j<-1 to 3
}
{
println("xxx")
}
  • 控制步长
1
2
3
4
5
6
7
8
//使用range控制for循环的步长
var range = new Range(1,10,2)
for (i<- range){
println(i)
}
for (i<-1 to 10 if i % 2 == 1){
println(i)
}

3.while循环控制

1
2
3
4
5
6
7
8
while(){

}
do{

}while()

两者区别:while先进行判断再执行 do while 先执行再判断

循环中断

1
2
3
4
5
6
7
8
9
10
11
	var  i =1;
breakable{
while (i<20){
println(i)
if(i%4==0){
break;
}
}
}

//需要导包 import util.control.Breaks._

4.异常处理

  1. 在scala中只有一个catch,但是有多个case,每个case可以用来匹配一种异常
1
2
3
4
5
6
7
8
9
10
11
12
try {
val i = 10/0;
}catch{
case exception: ArithmeticException =>{
println("出现除0异常")
}
case exception: NullPointerException =>{
println("空指针异常")
}
}finally {
println("最后要被执行的代码")
}
  1. scala中没有checked异常,都是在运行进行捕捉的
1
2
3
def test4():Unit={
throw new ArithmeticException();
}

三.方法的定义

基本语法

1
2
3
def methodName(arg1:type,arg2:type2)[:type]={

}

注意事项:

  • 当函数的返回值是unit是,=可以省略
1
2
3
4
5
6
7
8
 def test1(str:String):Unit={

println(str)
}
def test1(str:String){

println(str)
}
  • 返回值类型可以自动推断
1
2
3
4
5
6
7
8
9
// return可以省略
def add(a:Int,b:Int):Int={
a+b;
}
// 返回值类型可以省略 =不可以省略,否则返回类型为Unit
def add(a:Int,b:Int)={

a+b;
}
  • 递归一定要声明返回值类型
1
2
3
4
5
6
7
8
9
10
def fac(n:Int):Int={

if(n == 1){
return 1
}
if (n==2){
return 2
}
return fac(n-1)+fac(n-2)
}
  • 可以指定默认值
1
2
3
4
//如果指定实参会将默认值覆盖
def test2(str:String="test2")={
println(str)
}
  • 可变参数
1
2
3
def test3(str:String,str2:Any*)={

}

过程

返回值为Unit,忽略=

1
2
3
4
def test1(str:String){

println(str)
}

惰性函数

当函数的返回值被声明为lazy时,函数的执行将延迟,直到正在去取值的时候,函数才会执行

1
2
3
4
5
6
lazy val lazyVal = add(10,20);

def add(a:Int,b:Int)={

a+b
}

注意:不能修饰var变量

​ lazy还可以用来修饰变量

1
lazy val i = 1 //变量值的分配将会延迟

四.面向对象编程

1.类与对象

  • 类的定义

编写一个类,用scalac将器编译,再通过 javap -p 反编译查看生成的java代码

1
2
3
4
5
6
7
8
9
//scala 类
class Cat {

var name:String = _

var age:Int = _

var gender:String = _
}

javap -p Cat.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class com.hsj.scala.day1.day2.Cat {
 //属性
private java.lang.String name;
private int age;
private java.lang.String gender;
//get set方法
public java.lang.String name(); //name的get
public void name_$eq(java.lang.String);//name的set
public int age(); //age get
public void age_$eq(int);//age set
public java.lang.String gender();//gender get
public void gender_$eq(java.lang.String);gendet set
//构造器
public com.hsj.scala.day1.day2.Cat();//默认构造器
}

可以看到 属性默认是private,如果使用的是var,对应的会生成get set 方法

如果使用val,则只会生成get方法

  • 创建对象
1
2
3
4
def main(args: Array[String]): Unit = {
val cat:Cat = new Cat
var cat2:Cat = new Cat
}

推荐使用val,因为创建对象一般不会改变它的引用,只是改变它的属性值

2.构造器

scala的构造器与java的构造器有很多相同的地方,scala中的构造器可以定义多个,支持重载,这一点和java一样,但是scala也有自己的一些新特性,scala的构造器分为主构造器,辅助构造器,主构造器在类上声明。

1
2
3
4
5
6
7
8
9
10
11
12
class Cat(nameIn:String, ageIn:Int, gen:String){ //主构造器

var name:String = _

var age:Int = _

var gender:String = _

def this(){  //辅助构造器
this("jack",18,"male")
}
}

注意:

​ 在辅助构造器中一定要在第一行调用主构造器

主构造器中的参数

  • 如果主构造器中的参数没有加修饰符修饰,默认为局部变量
  • 主构造器中的参数使用var修饰,这个参数会升级成为成员变量,并且提供get set方法
  • 主构造器中的参数使用val修饰,这个参数会升级成为成员变量,只提供get 方法

get set方法

1
2
3
4
5
6
7
8
//get
val cat:Cat = new Cat
println(cat.name)
println(cat.age)
println(cat.gender)
//set
cat.name = "mary"
println(cat.name)

如果想要使用java风格的get set需要在属性字段上添加@BeanProperty

1
2
3
@BeanProperty var name:String = _

println(cat.getName)

对象的创建流程

  1. new 关键字创建对象
  2. 识别new后面的类是否被加载,如果没有被加载,先加载类
  3. 在内存为该对象分配空间
  4. 调用父类构造器进行初始化
  5. 调用构造器进行初始化
  6. 将该对象的地址赋值给一个变量

3.package

scala中的包与java中的包作用一样

  1. 区分相同名字的类
  2. 控制访问范围
  3. 类很多时,方便管理类

scala默认导入三个包,java.lang ,scala._ ,scala.predef

包的使用

1
2
3
4
5
6
7
8
9
10
package com.hsj.packageTest
package test{
class Test {

}

package scala{

}
}

注意事项:

package test{} :表示创建了包com.hsj.packageTest.test

在scala中,可以在一个文件中创建多个package,并且可以在包中创建类,特质,和对象

scala中子包可以访问父包中的内容,{ }体现了作用范围,当子包拥有与父包相同的类时,采用就近原则,如果希望指定哪个类,带上包名即可

如果想要在父包中使用子包的一些内容,需要在父包中import

包中不能定义变量和方法

包对象

由于包中不能定义变量和方法,使用包对象补足

每一个包都可以拥有一个包对象,名字必须相同,在包对象中定义的变量和方法可以在包中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package object scala{

var name:String ="scala";

def print():Unit={
println(name)
}
}
package scala{

object test{
def main(args: Array[String]): Unit = {
print()
}
}
}

scala中包的访问权限

  • 属性的访问权限为默认时,属性使用的是private修饰
  • 方法的访问权限为默认时,属性使用的是public修饰
  • private只有在类的内部和伴生对象中可用
  • protected 只有在子类可以访问,同包无法访问,这点和java不同
  • scala添加了包访问权限,表示属于那个包
1
2
3
4
5
6
7
8
9
10
11
12
package scala{

object test{
def main(args: Array[String]): Unit = {
var per:Persion = new Persion
println(per.name)
}
}
class Persion{
private [scala] var name:String = "jack"
}
}

4.伴生类与伴生对象

介绍

在scala中,类用class表示,object表示的一个静态的对象,如果class的名称与object的名称相同,该类被称为半生类,object被称为伴生对象。伴生对象的内容都是静态的,可以通过类名直接调用,类和其伴生对象可以互相访问私有属性,但必须存在同一个源文件中

1
2
3
4
5
6
7
8
9
10
11
object User{
var name:String = "jack"
def main(args: Array[String]): Unit = {

println(User.name)
}
}

class User( var name:String){

}

apply方法

使用apply(装配器,将属性装配到对象)方法,实现类名(参数)的方式创建对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
object User{

def apply():User=new User();

def apply(name:String): User = new User(name)

}

class User(){
var name:String = _;
def this(name:String){
this
this.name = name;
}
}

object test{
def main(args: Array[String]): Unit = {
val u1 = User("link")
println(u1.name)
}
}

5.trait(特质)

特质的声明

1
2
3
trait 特质名{

}

特质的使用

1
class 类名 extends 父类 with trait1 with trait2 with trait3...

特质中可以拥有抽象方法和具体方法

1
2
3
4
5
6
7
8
trait Persion {

def eat()

def sleep(): Unit ={
println("sleep~~~~")
}
}

动态混入

除了可以在类声明的时候继承特质,还可以在构建对象是动态的混入特质,达到扩展功能的效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
trait Persion {

def eat()

def sleep(): Unit ={
println("sleep~~~~")
}
}
trait Animal{
def fright(): Unit ={
println("fright~~~")
}

def run();
}
class P1 extends Persion{
override def eat(): Unit = {
println("eat~~~~~")
}

}
object P1{

def main(args: Array[String]): Unit = {
val p1 = new P1 with Animal{
override def run(): Unit = {

}
}

p1.eat()
p1.fright()
}
}

叠加特质:构建对象时混入多个特质,称为叠加特质

叠加特质的声明是从左到右,方法的执行是从右到左

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
object TestTrait {

def main(args: Array[String]): Unit = {
//声明从左到右,执行从右到左 类似栈
val abc = new ABC with A1 with A2

abc.test //执行的是A2中的test方法
}
}
class ABC{
}
trait Total{
def test;
}
trait A extends Total {
override def test: Unit ={
println("A test~~")
}
println("A~~")
}

trait A1 extends A{
override def test: Unit ={
println("A1 test~~")
super.test
}
println("A1~~")
}

trait A2 extends A{
override def test: Unit ={
println("A2 test~~")
super.test  
}
println("A2~~")
}

叠加特质一定要注意方法的执行顺序如上述代码中,先执行A2中的test,执行到 super.test的时候,执行的不一定是父特质的方法,它会根据混入的特质的顺序执行,执行的是它左边的特质中的test方法如果左边没有特质则执行父特质的方法。

上述代码执行顺序:

A2.test–>A1.test–>A.test

注意:A1和A2继承的是同一个父特质,如果不是同一个父特质,那么A2中的super执行的是父特质test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
object TestTrait {

def main(args: Array[String]): Unit = {
val abc = new ABC with A1 with A2
abc.test
}
}
class ABC{
}
trait Total{
def test;
}
trait A extends Total {
override def test: Unit ={
println("A test~~")
}
println("A~~")
}

trait B extends Total{
override def test: Unit ={
println("B test~~")
}
println("B~~")
}
trait A1 extends A{
override def test: Unit ={
println("A1 test~~")
super.test
}
println("A1~~")
}

trait A2 extends B{
override def test: Unit ={
println("A2 test~~")
super.test
}
println("A2~~")
}

执行顺序:A2.test—>B.test

富接口

富接口:该特质中既有抽象方法又有非抽象方法

1
2
3
4
5
6
trait Persion {
def eat()
def sleep(): Unit ={
println("sleep~~~~")
}
}

trait中的字段

trait中可以定义具体的字段和抽象的字段,一个对象如果混入该特质,就拥有了特质中的字段

1
2
3
4
5
6
7
8
9
10
11
12
13
object TestTrait2 extends App {
val param = new T with Param{
override val age: Int = 18
}
println(param.name)
}
trait Param{
val name:String = "jack"
val age:Int;
}
class T{

}

trait的构造顺序

创建对象时混入特质

  1. 调用当前类的超类的构造器
  2. 调用第一个trait的的超类构造器
  3. 调用第一个trait的构造器
  4. 调用第二个trait的超类构造器
  5. 调用第二个trait的构造器
  6. 调用当前类的构造

trait的扩展类

trait可以继承类,如果有那个类(A)混入了该trait,那么,这个类(A)会成为trait的超类的子类,

如果这个类(A)还继承了别的类,继承的这个类一定要是trait的超类的子类,否则会发生多继承的错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
object TestTrait3 {
def main(args: Array[String]): Unit = {
val a = new A
println(a.getMessage)
}
}
trait TraitException extends Exception{
override def getMessage: String = {
return "TraitException"
}
}

class A extends Exception with TraitException{

}

解决该情况在idea中编译时不报错的方式

1
2
3
4
5
6
7
// 自身类型
trait TraitException extends Exception{
this:Exception =>
override def getMessage: String = {
return "TraitException"
}
}

6.重写

  1. 在Scala中重写一个非抽象方法必须使用override修饰符。
1
class week extends month{ override def firstday = {...} }
  1. .重写包括字段和方法,但参数不同的方法可以不重写
1
2
3
4
5
6
class month{ 
def secondday(m:String)={...}
}
class week extends month{
def secondday ={...}
}
  1. 在Scala中调用超类的方法和Java一样,使用super关键字
  2. 只有主构造器可以调用超类的主构造器;
1
class Employee(name:String,age:Int,val salary:Double) extends Person(name:String,age:Int)
  1. 重写规则
1
2
3
4
5
6
7
重写 def 
用val :利用val能重写超类用没有参数的方法(getter)
用def:子类的方法与超类方法重名
重写val
用val:子类的一个私有字段与超类的字段重名,getter方法重写超类的getter方法
重写var
用var:且当超类的var是抽象的才能被重写,否则超类的var都会被继承

7.嵌套类

内部类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class OuterClass {
//别名
outerClass =>
var name:String = "jack"
class InnerClass{
def inner: Unit ={

//访问外部类的属性 1
println(OuterClass.this.name)

//访问外部类的属性 2 别名
println(outerClass.name)
}
}
def tets: Unit ={
//创建内部类的对象
 val outerClass = new OuterClass
val innerClass:outerClass.InnerClass = new outerClass.InnerClass
}
}

静态内部类

1
2
3
4
5
6
7
8
9
10
11
12
class OuterClass {
def tets: Unit ={
//创建静态内部类对象
val staticInnerClass = new OuterClass.StaticInnerClass
}
}
//伴生对象
object OuterClass{
class StaticInnerClass{

}
}

类型投影

创建内部类时,创建的内部类会与外部类实例产生关联

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class OuterClass2 {

class InnerClass{

def test(innerClass: InnerClass): Unit ={
println(innerClass)
}
}
}
object OuterClassDemo{

def main(args: Array[String]): Unit = {

val outer1 = new OuterClass2
val outer2 = new OuterClass2
val inner1 = new outer1.InnerClass
val inner2 = new outer2.InnerClass
inner1.test(inner1);
inner2.test(inner2)
//报错 由于inner1已经和外部类实例outer1关联,inner1调用test方法传入的参数只能是outer1.InnerClass
// inner1.test(inner2)
}
}

为了解决上述问题,只需要在方法声明上添加 外部类#内部类,表示忽略外部类实例与内部类实例的关系

1
2
3
def test(innerClass: OuterClass2#InnerClass): Unit ={
println(innerClass)
}

## 五.隐式转换和隐式值

1.隐式转换解决数据类型的转换

1
2
3
4
5
6
7
8
9
10
11
12
object ImpliciteTest{

def main(args: Array[String]): Unit = {

//在底层生成f1$1(double d)的方法,将double强转为int
implicit def f1(d:Double):Int={
d.toInt
}
//底层调用f1$1(2.5)的方法
val i = 2.5;
}
}

2.隐式值

隐式值也叫隐式变量,当一个方法在省略隐式参数时,会自动搜索作用域的隐式参数作为缺省参数

1
2
3
4
5
6
7
8
9
 def main(args: Array[String]): Unit = {

implicit val name:String = "lix"

def test(implicit name:String): Unit ={
println(name)
}
test
}

3.隐式值,默认值,传值的优先级

传值>隐式值>默认值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 def main(args: Array[String]): Unit = {

implicit val name:String = "lix"

def test(implicit name:String="jack"): Unit ={
println(name)
}
test //结果为lix
}

def main(args: Array[String]): Unit = {

implicit val name:String = "lix"

def test(implicit name:String="jack"): Unit ={
println(name)
}
test("xxx") //结果为xxx
}

4.隐式类

隐式类必须定义在类,伴生对象,包对象中,不能作为一个顶级类存在,并且隐式类有且只有一个构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
object ImpliciteTest{
def main(args: Array[String]): Unit = {
// implicit 修饰的是构造器
implicit class A(b: B){
def test: Unit ={
println("隐式类的调用")
}
}
val b = new B
b.test
}
}
class B{

}

总结:

隐式类的转换时机

  1. 方法中的参数与它需要的参数类型不一致,或者是在赋值的时候
  2. 当一个对象调用本类不存在的方法或参数时

六.数据结构

1.集合关系

scala集合包括可变集合和不可变集合,scala集合有三大类.序列seq,集合set,映射map

可变集合:scala.collection.mutable,集合本身不能动态改变,java中的数组

不可变集合:scala.collection.immutable,集合本身可以动态改变,java中的ArrayList

不可变集合预览图

可变集合预览图

2.数组

不可变数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 定义方式一
val a1 = new Array[String](5)
// 定义方式二
val a2 = Array("hello","scala")

//遍历方式一
for (elem <- a2) {
println(elem)
}
//遍历方式二
for (i<-0 to a2.length){
println(a2(i))
}
//遍历方式三
a2.foreach(println)

可变数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
val ab1 = ArrayBuffer[String]("hello","scala")
val ab2 = ArrayBuffer[String]("hello2","scala2")

//append 在原数组后面追加数据
ab1.append("ab1","append")

//remove 删除数组指定下标的元素并将其返回
val res1: String = ab1.remove(0)

//从数组下标为0的位置开始,删除两个元素
val res2 = ab1.remove(0,2);

 //drop 删除指定个数的元素 返回删除的元素,形成一个新的数组 原数组不变
val res3:ArrayBuffer[String] = ab1.drop(2)

/dropRight 从右边删除指定个数的元素 返回删除的元素,形成一个新的数组
val ab2 = ab.dropRight(3)
//可变数组转化为不可变数组 原数组不发生改变
val newArr: Array[String] = ab1.toArray

//将不可变数组转化为可变数组,原数组不发生改变
val newBuf = newArr.toBuffer

//在ab1后面追加集合ab2
ab1 ++= ab2;

//在ab1前面追加集合ab2
ab1.++=:(ab2) // ab2 ++=: ab1

//在集合ab1后面追加集合ab2并返回一个新的集合
val ab3 = ab1.++(ab2)

//在集合ab1前面追加集合ab2并返回一个新的集合
val ab4 = ab1.++:(ab2)

ab1.+=("hello+=")
ab1.+=:("hello+=:")
val ab5 = ab1.+("hello+");
val ab6 = ab1.+("hello+:")

二维数组

1
2
3
4
5
6
7
8
 val arr: Array[Array[String]] = Array.ofDim[String](5,4)

for (item<-arr){
for (res<-item){
print(res+"\t")
}
println
}

3.元组

元组可以存放各种数据类型不同的数据,元组将多个无关的数据封装成为一个整体,元组最多只能有22个元素

1
2
3
4
5
6
7
8
9
10
11
12
// 元组的定义
val t1: Tuple1[String] = new Tuple1("1")
val t3: (Int, Int, String) = new Tuple3(1,2,"str")
val t4 = ("1",2.1,"str","hello")

//元组的访问
println(t4._1)
println(t4._2)
//元组的遍历
for (i<-t3.productIterator){
println(i)
}

4.List

当我们定义一个List时在scala中是不可变的,如果需要使用可变的,需要使用ListBuffer

不可变集合List

1
2
3
4
5
6
7
8
9
10
//不可变集合
val l1 = List(1,2,"abc")

val list = List(1,2,3,"str")
// :: 集合对象一定要放在右边 如果不加Nil会形成一个集合,如果加Nil,会形多个集合的集合
val list2 = 4::5::list //List(4, 5, 1, 2, 3, str)
val list3 = 4::5::list::Nil //List(4, 5, List(1, 2, 3, str))
//::: 将集合中的每一个元素添加到集合中去 注意 ::: 左右两边都要是集合
val list4 = 4::5::immutable.Nil:::list
println(list4)

可变集合ListBuffer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//可变集合
val lb = ListBuffer(11,22,"abc2")
println(lb.length)

//;list元素的访问
println(lb(1))

//遍历
for (i<-lb){
println(i)
}
// 获取除第一个元素外的其它元素
val tail = lb.tail
println(tail)

// 从右边开始获取n个元素
val tr = lb.takeRight(2)
println(tr)

// 获取n个元素
val t = lb.take(2)
println(t)
//获取符合条件的元素
val tw = lb.takeWhile(i=>i==null)
println(tw)
println(lb)

println("===========================")

//删除n个元素,将删除后的原集合剩下的元素返回
val ld = lb.drop(2)
println(ld)
//从右边开始删除n个元素,将删除后的原集合剩下的元素返回
val dr = lb.dropRight(2)
println(dr)

//删除满足条件的元素,将删除后的原集合剩下的元素返回
val dw = lb.dropWhile(elem => elem!=null)
println(dw)

映射Map

scala中的集合默认是不可变的,如果需要使用可变的,可以使用scala.collection.mutabl下的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//定义
val map1 = Map((1,"li"),(2,"xi"),(3,"jiafeng"))
map1.+((4,"meng"))
val map2 = mutable.Map((1,"lo2"),(2,"xo2"))
//取值
// map1(4) 如果不存在 NoSuchElementException
// 如果存在 返回Some 不存在返回None
println(map1.get(3))
// 如果值存在 返回值 不存在 返回 默认值
println(map1.getOrElse(4,"NULL"))

//添加|修改 如果不存在则添加,存在则覆盖
map2(1) = "lolo"
println(map2)
map2.+=((1,"xxx"));
println(map2)

//遍历方式
for ((k,v)<- map2){
println(k+" "+v)
}
for (m<- map2){
println(m._1+" "+m._2)
}
for (k <- map2.keys){
println(k+" "+map2(k))
}

Set

不重复元素的集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//定义
//不可变集合
val set1 = Set(6,1,2,3,1)
//不可变集合
val set2 = mutable.Set(6,1,2,3,1)
println(set1)

//增
set2.+=(4)
set2.add(5)
//删
set2.-=(4)
set2.remove(5)
println(set2)
//遍历
for (i<- set2){
println(i)
}

集合元素的操作

  1. 将List(1,2,3)中的所有元素*2
1
2
3
val list =  List(1,2,3,4,5)
val res1 = list.map(i => i*2)
println(res1)
  1. 将List(“hello”,”world”,”scala”)中的所有单词,全部转为大写
1
2
val res2 = list2.map(word=>word.toUpperCase)
println(res2)
  1. flatMap 扁平化
1
2
3
4
5
val res3 =  list2.flatMap(s=>s.toUpperCase)
println(res3) // List(H, E, L, L, O, W, O, R, L, D, S, C, A, L, A)

val list3 = List(List(1,2,3),List(4,5,6),List(7,8,9))
println(list3.flatMap(i => i.map(j=>j*2)))//List(2, 4, 6, 8, 10, 12, 14, 16, 18)
  1. filter 过滤
1
2
3
val list =  List(1,2,3,4,5)
val res = list.filter(i => i%2==0)
println(res)//List(2, 4)
  1. reduce
1
2
3
4
5
6
7
val list =  List(1,2,3,4,5)
val res = list.reduce((a,b)=>a+b)
println(res)
// ((((1-2)-3)-4)-5)
println(list.reduceLeft((a,b)=>a-b))
//(1-(2-(3-(4-5))))
println(list.reduceRight((a,b)=>a-b))
  1. fold

把上一步的返回值交给下一步,继续参与运算

1
2
3
4
5
6
7
8
9
10
11
val list =  List(1,2,3,4,5)
//类似  List(1,1,2,3,4,5).reduceLeft((num1,num2)=>num1-num2))
println(list.fold(1)((num1,num2)=>num1-num2))
//类似List(1,1,2,3,4,5).reduceLeft((num1,num2)=>num1-num2))
println(list.foldLeft(1)((num1,num2)=>num1-num2))
//类似List(1,2,3,4,5,1).reduceLeft((num1,num2)=>num1-num2)) 
println(list.foldLeft(1)((num1,num2)=>num1-num2))

//foldLeft 的缩写为 /: foldRight 的缩写为 \:
println(list./:(1)((num1,num2)=>num1-num2))
println(list.:\(1)((num1,num2)=>num1-num2))
  1. scan

对集合中的所有元素做fold操作,但是会保留每步的操作结果

1
2
3
4
5
6
7
val list =  List(1,2,3,4,5)
val res: List[Int] = list.scan(5)((n1, n2)=>n1-n2)
println(res)//List(5, 4, 2, -1, -5, -10)
val res2 = list.scanLeft(5)((n1, n2)=>n1-n2)
println(res2)//List(5, 4, 2, -1, -5, -10)
val res3 = list.scanRight(5)((n1, n2)=>n1-n2)
println(res3)//List(-2, 3, -1, 4, 0, 5)
  1. 拉链操作
1
2
3
4
5
6
7
8
9
10
val l1 =  List(1,2,3,4)
val l2 = List("a","b","c","d","e")
println(l1.zip(l2)) // 会造成数据丢失List((1,a), (2,b), (3,c), (4,d))
// 三个参数
println(l1.zipAll(l2,"l1N","l2N"))//List((1,a), (2,b), (3,c), (4,d), (l1N,e))

val res = l1.zipAll(l2,0,"l2N")
println(res)//List((1,a), (2,b), (3,c), (4,d), (0,e))
val res2: (List[Int], List[String]) = res.unzip
println(res2)// (List(1, 2, 3, 4, 0),List(a, b, c, d, e))
  1. java集合与scala集合互相转换
1
2
3
4
5
6
7
8
// java集合与scala集合的互相转换
import scala.collection.JavaConverters._
val scalaList = List(1,2,3,4,5,6)
val javaList: util.List[Int] = scalaList.asJava
val scalaList2 = javaList.asScala
val scalaMap = Map((1,"a"),(2,"b"))
val javaMap = scalaMap.asJava
val scalaMap2 = javaMap.asScala

七.模式匹配

1.基本介绍

采用match关键字声明,每个分支采用case关键字声明,需要匹配时从第一个case开始,如果匹配成功,执行相应的逻辑代码。

1
2
3
4
5
6
7
8
9
val oper:Char = '2'
val n1:Int = 10
val n2:Int =20

val res = oper match {
case '+' => n1+n2
case '-' => n1-n2
case _ => println("error")
}

注意:

  1. 如果所有的case都不匹配则执行case _ ,如果没有case _ 会抛出一个MatchException
  2. 如果case _后面有条件判断(守卫),则case _不是默认匹配
  3. case后面跟变量,match前面的值会赋值给变量
1
2
3
4
5
oper match {
case a => if (a.equals('+')) n1+n2
case b => if (b.equals('-')) n1-n2
case _ => println("error")
}

2.类型匹配

在类型匹配时,编译器会自动判断有没有可能匹配的类型

1
2
3
4
5
6
7
8
    //类型匹配
val a:Int = 1
a match {
case t1:Int => "Int"
// case t2:String =>"String"
case t3:Long => “long”
// case t4:List[Int] => "list[Int]"
}

3.数组匹配

1
2
3
4
5
6
7
8
9
10
val arrs = Array(Array(0,1),Array(0),Array(0,1,2,3))
for (arr<-arrs){
val res = arr match {
case Array(x) => x
case Array(x,y) => ArrayBuffer(y,x)
case Array(0,_*) => "以0开头"
case _ => "other"
}
println(res)
}

4.列表匹配

拥有unapply方法的集合才能够进行匹配

1
2
3
4
5
6
7
8
9
10
11
val lists = List(List(1),List(1,2),List(0,List(1,2)))

for (list<-lists){
val res = list match {
case x::Nil => x
case 0::tail=> "..."
case x::y::Nil => x+""+y
case _ => "some else "
}
println(res)
}

5.对象匹配

对象匹配需要通过unapply方法进行匹配,unapply是一个对象提取器,返回some则表示匹配成功,返回None表示匹配失败。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
object Nums{

def apply(x:Double): Double = {

Math.pow(x,2)
}
def unapply(arg: Double): Option[Double] ={
Some(Math.sqrt(arg))
// None
}
}

val nums = Nums(6)
nums match {
// 调用unapply方法,将返回值赋值给n
case Nums(n) => println(n)
case _ => println("else")
}

6.样例类

样例类使用case声明的一种特殊的类,构造器中的每个参数默都是val,它提供了apply方法用于创建对象,提供unapply方法用于进行模式匹配,此外,它还提供了equals ,copy,toString等方法。样例类对应了两个class文件,类名.class,类名$.class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
object TestCaseClass {

def main(args: Array[String]): Unit = {

for (i<-(Array(Dollar(1000,"$"),Currency(1000,"RMB")))){
i match {
case Dollar(x,y) => println(x+" "+y)
case Currency(x,y) => println(x+" "+y)
}
}
}
}
case class Dollar(value:Double,unit:String)
case class Currency(value:Double,unit:String)

样例类提供了copy方法,构造新的对象

1
2
val dollar = Dollar(20000,"$")
val ndollar = dollar.copy()

7.sealed

样本类的超类被封闭(sealed),封闭类除类定义文件外不能添加子类.模式匹配完成后需要确保所有情况皆被考虑,因此Scala编译器会检测match表达式所遗漏的模式组合

1
2
3
4
5
6
7
8
9
10
11
12
13
sealed abstract class Expr  
case class Number( n :Int) extends Expr
case class Sum(e1 : Expr , e2 : Expr) extends Expr
case class Mul(e1 : Expr , e2 : Expr) extends Expr

如何定义存在可能样本遗漏的模式匹配
def getType(a:Expr):String = a match{
case Number(n) => “Number“
case Sum(m,n) => “Sum“
}
warning : match is not exhaustive
可以添加注解,消除warning
def getType(a:Expr):String = (a: @unchecked) match {...}

8.偏函数

被包在花括号内的一组case语句是一个偏函数–一个并非对所有输入值都有定义的函数。它是PartialFuncation[A,B]类的一个实例。(A是参数类型,B是返回类型).Scala中的PartialFunction是一个Trait,其的类型为PartialFunction[A,B],其中接收一个类型为A的参数,返回一个类型为B的结果

1
2
3
4
5
6
7
8
val pf :PartialFunction[Int,String] ={
case 1 => "a"
case 2 => "b"
case 3 => "c"
case 4 => "d"
case _ => "other"
}
println(pf(1))

偏函数中常用的方法

  • isDefinedAt:这个函数的作用是判断传入来的参数是否在这个偏函数所处理的范围内。刚才定义的pf来尝试使用isDefinedAt(),只要是Int类型都是正确的,因为有case _=> “Other”这一句。如果换成其他类型就会报错。

  • orElse : 将多个偏函数组合起来使用,效果类似case语句。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     val onePF:PartialFunction[Int,String] = {
    case 1=>"One"
    }
    val twoPF:PartialFunction[Int,String] = {
    case 2=>"Two"
    }
    val threePF:PartialFunction[Int,String] = {
    case 3=>"Two"
    }
    val otherPF:PartialFunction[Int,String] = {
    case _=>"other"
    }

    //作用与上面的偏函数作用一致
    val newPF = onePF orElse twoPF orElse threePF orElse otherPF

  • andThen: 相当于方法的连续调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
val pf :PartialFunction[Int,String] ={

case 1 => "a"

case 2 => "b"

case 3 => "c"

case 4 => "d"

case _ => "other"

}

val pf2:PartialFunction[String,String] = {

case a if(a.eq("a")) => "a 1"

case b if(b.eq("b")) => "b 2"

case c if(c.eq("c")) => "c 3"

case d if(d.eq("d")) => "d 4"

case _ => "other"

}

val newpf = pf.andThen(pf2)

println(newpf(1)) //结果为 a 1
  • applyOrElse:它接收2个参数,第一个是调用的参数,第二个是个回调函数。如果第一个调用的参数匹配,返回匹配的值,否则调用回调函数。
1
println(pf.applyOrElse(10,{a:Int => "ten"})) // other

8.函数式编程

闭包函数

闭包函数 返回的函数体引用到外部的参数,参数与函数体形成一个闭包

1
2
3
def cosure(x:Int)={
(y:Int) => x*y
}

柯里化函数

将接受多个参数的函数转化成接受一个参数的函数

1
2
3
4
5
6
7
 def mul(a:Int,b:Int,c:Int)=a*b*c

//def mut1(a:Int)(b:Int) = a*b
def mul1(a:Int)=(b:Int) => a*b

//def mut2(a:Int)(b:Int)(c:Int) = a*b*c
def mut2(a:Int) = (b:Int) => (c:Int) => a*b*c

9.类型系统

1.泛型

  • 上界: A<:T;只能传入T的子类和本身
1
2
3
4
5
6
7
8
9
10
11
12
13
14
object TestShangJie {

def main(args: Array[String]): Unit = {
val compareUtil = new CompareUtil[String]
println(compareUtil.greater("Hello","acv"))
}
}
// 只用实现了Comparable的子类才可以使用该类 Comparable为上界
class CompareUtil[T<:Comparable[T]]{

def greater(a:T,b:T):T={
if (a.compareTo(b)>0) a else b
}
}
  • 下界 :A>:T;只能传入T的父类和本身
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Earth{
def sound(): Unit ={
println("earth sound")
}
}
class Animal extends Earth{
override def sound(): Unit = {
println("animal sound")
}
}
class Dog extends Animal{

override def sound(): Unit = {
println("dog sound")
}
}

def main(args: Array[String]): Unit = {
val compareUtil = new CompareUtil[String]
println(compareUtil.greater("Hello","acv"))

val earths = Array[Earth](new Earth,new Earth)
val animals = Array[Animal](new Animal,new Animal)
val dogs = Array[Dog](new Dog,new Dog)
testlLowerBound(earths).map(_.sound())
testlLowerBound(animals).map(_.sound())
// testlLowerBound(dogs).map(_.sound()) 报错
}

def testlLowerBound[T>:Animal](array: Array[T]): Array[T] ={
array
}
  • 视图界定:A<%T;支持上界和隐式转换,在利用Compareble进行比较的时候,由于Int不是Comparable[Int]的子类型,不能直接进行传参比较。而scala中会将Int隐式转化成RichInt,利用视图界定,可以有效的解决Comparable不能直接传Int的问题
1
2
3
4
5
6
7
// 允许传入Comparable的子类和
class CompareUtil[T<% Comparable[T]]{

def greater(a:T,b:T):T={
if (a.compareTo(b)>0) a else b
}
}
  • 上下文界定:T:M ;要求必须存在一个类型为M[T]的”隐式值”

写法一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 隐式值
implicit val compare = new Ordering[Emp]{
override def compare(x: Emp, y: Emp): Int = {
x.age-y.age
}
}
// [T:Ordering] =》 (implicit ord:Ordering[T])
def comparatorEmp[T:Ordering](t1:T,t2:T)(implicit ord:Ordering[T]): T ={
if(ord.compare(t1,t2)>0) t1 else t2
}
class Emp(var age:Int){
}

// 方法调用
def main(args: Array[String]): Unit = {
val emp1 = new Emp(18)
val emp2 = new Emp(20)
val res = comparatorEmp(emp1,emp2)
println(res)
}

写法二:

1
2
3
4
5
def comparatorEmp[T:Ordering](t1:T,t2:T): T ={

def helper(implicit ord:Ordering[T]):T = if(ord.compare(t1,t2)>0) t1 else t2
helper
}

2.形变

在java中 泛型不存在继承关系,即不存在多态但是在scala中 允许泛型之间有联系,使用型变来描述这种联系

  • 协变:C[+T]:如果A是B的子类,那么C[A]是C[B]的子类。也就是被参数化类型的泛化方向与参数类型的方向是一致的,所以称为协变。
  • 逆变:C[-T]:如果A是B的子类,那么C[B]是C[A]的子类。也就是被参数化类型的泛化方向与参数类型的方向是相反的,所以称为逆变。
  • 不变:C[T]:无论A和B是什么关系,C[A]和C[B]没有从属关系
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Engineer
class Expert extends Engineer
class Meeting[+T]//可以传入T或T的子类
object MeetingTest{
def participateMeeting(meeting:Meeting[Expert])= println("welcome")

def main(args: Array[String]): Unit = {
val m = new Meeting[Expert]
val m1 = new Meeting[Engineer]

participateMeeting(m)
participateMeeting(m1)
}
}

3.类型约束

类型约束: 放到隐式关键字后面

​ A =:= T A是否等于T
A <:< T A是否为T的子类
A <%< T A是否可以隐式转化成T
注意:使用类型约束需要使用implicit关键字

1
2


文章目录
  1. 1. 一.变量
    1. 1.1. scala的数据类型
      1. 1.1.1. 数据类型列表
    2. 1.2. 变量声明
      1. 1.2.1. 键盘输入语句
  2. 2. 二.程序流程控制
    1. 2.1. 1. if-else
    2. 2.2. 2.for循环控制
    3. 2.3. 3.while循环控制
    4. 2.4. 4.异常处理
  3. 3. 三.方法的定义
    1. 3.0.1. 基本语法
  4. 3.1. 过程
  5. 3.2. 惰性函数
  • 4. 四.面向对象编程
    1. 4.1. 1.类与对象
    2. 4.2. 2.构造器
      1. 4.2.1. 主构造器中的参数
      2. 4.2.2. get set方法
      3. 4.2.3. 对象的创建流程
    3. 4.3. 3.package
      1. 4.3.1. 包的使用
      2. 4.3.2. 包对象
      3. 4.3.3. scala中包的访问权限
    4. 4.4. 4.伴生类与伴生对象
      1. 4.4.1. 介绍
      2. 4.4.2. apply方法
    5. 4.5. 5.trait(特质)
      1. 4.5.1. 特质的声明
      2. 4.5.2. 特质的使用
      3. 4.5.3. 动态混入
      4. 4.5.4. 富接口
      5. 4.5.5. trait中的字段
      6. 4.5.6. trait的构造顺序
      7. 4.5.7. trait的扩展类
    6. 4.6. 6.重写
    7. 4.7. 7.嵌套类
      1. 4.7.1. 内部类
      2. 4.7.2. 静态内部类
      3. 4.7.3. 类型投影
      4. 4.7.4. 1.隐式转换解决数据类型的转换
      5. 4.7.5. 2.隐式值
      6. 4.7.6. 3.隐式值,默认值,传值的优先级
      7. 4.7.7. 4.隐式类
  • 5. 六.数据结构
    1. 5.1. 1.集合关系
    2. 5.2. 2.数组
      1. 5.2.1. 不可变数组
      2. 5.2.2. 可变数组
      3. 5.2.3. 二维数组
    3. 5.3. 3.元组
    4. 5.4. 4.List
      1. 5.4.1. 不可变集合List
      2. 5.4.2. 可变集合ListBuffer
      3. 5.4.3. 映射Map
      4. 5.4.4. Set
      5. 5.4.5. 集合元素的操作
  • 6. 七.模式匹配
    1. 6.1. 1.基本介绍
    2. 6.2. 2.类型匹配
    3. 6.3. 3.数组匹配
    4. 6.4. 4.列表匹配
    5. 6.5. 5.对象匹配
    6. 6.6. 6.样例类
    7. 6.7. 7.sealed
    8. 6.8. 8.偏函数
  • 7. 8.函数式编程
    1. 7.1. 闭包函数
    2. 7.2. 柯里化函数
  • 8. 9.类型系统
    1. 8.1. 1.泛型
    2. 8.2. 2.形变
  • 9. 3.类型约束
  • |
    载入天数...载入时分秒...