Scala’s most basic introductory tutorial

Article directory

1. Introduction

1 Overview

Official programming guide https://www.scala-lang.org/

Scala combines object-oriented and functional programming into a concise high-level language.

The language features are as follows:

(1) Scala, like Java, is a JVM language. It needs to be compiled into a class bytecode file before use, and Scala can directly call Java's class library.

(2) Scala supports two programming paradigms, object-oriented and functional programming.

(3) The Scala language is more concise and efficient; the syntax can be simplified, and the idea of ​​functional programming makes the code structure concise.

(4) The author Martin Odesky designed Scala based on the design ideas of Java. At the same time, excellent design also promoted the development of the Java language.

2. Idea environment

  1. First make sure that JDK1.8 is installed successfully
  2. Download the corresponding Scala installation file scala-2.12.10.zip
  3. Unzip scala-2.12.10.zip to any path without Chinese characters, such as D:\Tools
  4. Configure Scala environment variables

New Project
Insert image description here

Add scala plugin
Insert image description here

Add project support
Insert image description here

Add Scala installation package
Insert image description here

Create a new Scala folder and specify it as a source file
Insert image description here

Create new code file
Insert image description here

object HelloWorld {
    
    


  def main(args: Array[String]): Unit = {
    
    
    // java的方法调用
    System.out.println("hello scala")

    // scala的方法调用
    println("hello scala")
  }
}

2. Variables and data types

1. Comments

basic grammar

基本语法
(1)单行注释://
(2)多行注释:/* */
(3)文档注释:/**
	*
    */

2. Variables and constants (emphasis)

**Constant:** A variable whose value will not change during program execution.

1) Basic grammar

var 变量名 [: 变量类型] = 初始值		var i:Int = 10
val 常量名 [: 常量类型] = 初始值		val j:Int = 20

Note: Do not use variables where constants can be used.

feature:

  • When declaring a variable, the type can be omitted and the compiler will automatically deduce it, that is, type deduction.
  • Once the type is determined, it cannot be modified, indicating that Scala is a strong data type language.
  • When a variable is declared, it must have an initial value.
  • When declaring/defining a variable, you can use var or val to modify it. Variables modified by var can be changed, and variables modified by val cannot be changed.
  • The object reference modified by var can be changed, but the object modified by val cannot be changed, but the state (value) of the object can be changed. (For example: custom objects, arrays, collections, etc.).

3. Naming convention for identifiers

  • Begins with a letter or underscore, followed by letters, numbers, and underscores
  • Start with an operator and contain only operators (+ - * / # !, etc.)
  • Any string enclosed in backticks ...., even Scala keywords (39) will work

4. Keywords (39)

  • package, import, class, object, trait, extends, with, type, for
  • private, protected, abstract, sealed, final, implicit, lazy, override
  • try, catch, finally, throw
  • if, else, match, case, do, while, for, return, yield
  • def, choice, where
  • this, super
  • new
  • true, false, null

5. String output

1. String, concatenated by + sign

//  1、字符串,通过+号拼接
System.out.println()
println("hello" + "world")

2. Repeated string splicing

//  2、重复字符串拼接
println("lilei " * 20)

3. Printf usage: string, pass value through %

//  3、printf用法:字符串,通过%传值
printf("name:%s age %d\n", "linlai", 8)

4. String template (interpolated string): get variable value through $

s"": Indicates that the current string needs to be calculated.

$name: Take the variable namevalue and assign it to the string.

${age+1}: Take the variable agevalue and calculate +1.

//  4、字符串模板(插值字符串):通过$获取变量值
val name = "linhai"
val age = 8
val s1 = s"name:$name,age:${
      
      age}"
println(s1)
val s2 = s"name:${
      
      name + 1},age:${
      
      age + 2}"
println(s2)

5. Multi-line string

//	5、多行字符串
println("我" +
  "是" +
  "中国人")
println(
  """我
    |是
    |中国
    |人
    |""".stripMargin)
val margin =
  """
    |我
    |是
    |中国人
    |""".stripMargin

6. Data type

Insert image description here

6.1 Integer types (Byte, Short, Int, Long)
type of data describe
Byte [1] 8-bit signed two's complement integer. The value range is -128 to 127
Short [2] 16-bit signed two's complement integer. The value range is -32768 to 32767
Int [4] 32-bit signed two's complement integer. The value range is -2147483648 to 2147483647
Long [8] 64-bit signed two's complement integer. The numerical range is -9223372036854775808 to 9223372036854775807 = 2 raised to the (64-1) power -1
val a1: Byte = 1
val a2: Short = 2
val a3: Int = 3
val a4: Long = 4
6.2 Floating point types (Float, Double)
type of data describe
Float [4] 32-bit, IEEE 754 standard single-precision floating point number
Double [8] 64-bit IEEE 754 standard double-precision floating point number
val b1: Double = 3.14
//  默认为Double类型
val b2 = 3.14
val b3: Float = 3.14f
6.3 Character type (Char)

The character type can represent a single character, and the character type is Char.

val c1: Char = 'a'
val c2: Char = 523
val c3: Char = '\t'
val c4: Char = '\n'
val c5: Char = '\\'
val c6 = '\"'
6.4 Boolean type (Boolean)
  • The Boolean type is also called the Boolean type. The Boolean type data only allows the values ​​​​true and false.
  • The boolean type occupies 1 byte.
val bo1: Boolean = true
val bo2: Boolean = false
6.5 Unit type, Null type and Nothing type (emphasis)
type of data describe
Unit Indicates no value, which is equivalent to void in other languages. Used as the result type for methods that return no results. Unit has only one instance value, written as ().
Null null , the Null type has only one instance value null
Nothing The Nothing type is at the lowest level of Scala's class hierarchy; it is a subtype of any other type. When a function determines that there is no normal return value, we can use Nothing to specify the return type. This has the advantage that we can assign the returned value (exception) to other functions or variables (compatibility)
  • The Unit type is used to identify procedures, that is, functions that do not have a clear return value. Even if there is a value, it does not receive a value.
val u1: Unit = {
    
    
  10
  println(10)
}
println(u1)
// 如果标记对象的类型是unit的话  后面有返回值也没法接收
// unit虽然是数值类型  但是可以接收引用数据类型   因为都是表示不接收返回值
val u2: Unit = 10
println(u2)

Print the result:

10
()
()
  • The Null class has only one instance object, and Null is similar to the null reference in Java . Null can be assigned to any reference type (AnyRef), but cannot be assigned to a value type (AnyVal).
var n2: String = "bb"
n2 = "cc"
n2 = null

//	值类型不能等于null,idea不会识别报错  编译器会报错
var i4 = 10
//        i4 = null
  • Nothing can be used as the return type of a method that does not have a normal return value. It tells you very intuitively that this method will not return normally, and because Nothing is a subclass of any other type, it is also compatible with methods that require return values.
//	可以用来抛出异常
//	可以接收任意数据类型(Int使用Nothing接收)
val value: Nothing = {
    
    
  println("hello")
  1 + 1
  throw new RuntimeException()
}

7. Type conversion

7.1 Automatic conversion of numerical types

When a Scala program performs assignment or operation, a small-precision type is automatically converted into a large-precision numerical type. This is automatic type conversion (implicit conversion). Data types are sorted by precision (capacity) size as:

Insert image description here

  • Automatic promotion principle: When there are multiple types of data mixed operations, the system will first automatically convert all data into the data type with greater accuracy, and then perform calculations.
  • When assigning a numerical type with high precision to a numerical type with small precision, an error will be reported, otherwise automatic type conversion will be performed.
  • There is no automatic conversion between (byte, short) and char.
  • Byte, short, and char can be calculated. During calculation, they are first converted to int type.
//  自动类型提升,以最大的类型进行结果存储
val f1: Float = 1 + 1L + 3.14f
val f2: Double = 1 + 1L + 3.14
val f3: Double = 1 + 1L + 3.14f + 3.14
//  把精度大的赋值给精度小的会报错,反之会进行类型转换
val i = 10
val b: Double = i
//  (byte、short、char)之间不会相互转换
val b1: Byte = 10
val c1: Char = 20
//  byte,short,char他们三者可以计算,在计算时首先转换为int类型。
val b2: Byte = 20
val i1: Int = b1 + b2
7.2 Forced type conversion

The reverse process of automatic type conversion, converting a numerical type with high precision into a numerical type with small precision. A forced conversion function must be added when using it, but it may cause precision reduction or overflow, so be careful.

Java  :  int num = (int)2.5
Scala :  var num : Int = 2.7.toInt
//  1、将高精度转为低精度,就需要使用强制转换
val int = 2.99.toInt
val int1 = (10 * 3.5 + 6 * 1.5).toInt
7.3 Conversion between numerical type and String type
  • Convert the basic type to String type (syntax: add the value of the basic type + "").

  • String type to basic numeric type (syntax: s1.toInt, s1.toFloat, s1.toDouble, s1.toByte, s1.toLong, s1.toShort).

//  1、基本类型转为String类型
val str = 1 + ""
//  2、string类型转为数值类型
val d1 = "3.14".toDouble
val d2 = "1".toInt
//  3、小数类型,需先转为Double再转为Int类型
val i1 = "3.14".toDouble.toInt
//  标记为f的float数能够识别
val i02 = "12.5f".toFloat

3. Operators

1. Arithmetic operators

operator Operation example result
+ Positive name +3 3
- negative b=4; -b -4
+ add 5+5 10
- reduce 6-4 2
* take 3*4 12
/ remove 5/5 1
% Modulo (remainder) 7%5 2
+ String addition “He”+”I cry” “Hello”

2. Relational operators

operator Operation example result
== equal to 4==3 false
!= not equal to 4!=3 true
< less than 4<3 false
> more than the 4>3 true
<= less than or equal to 4<=3 false
>= greater or equal to 4>=3 true
//	==比较两个变量本身的值,即两个对象在内存中的首地址;
//	equals比较字符串中所包含的内容是否相同。
println("a".equals("b"))
println("a" == "b")

3. Logical operators

operator describe Example
&& logical AND (A && B) The operation result is false
|| logical or (A || B) The operation result is true
! logical negation !(A && B) The operation result is true

4. Assignment operator

operator describe Example
= Simple assignment operator, assigns the value of an expression to an lvalue C = A + B assigns the result of the expression A + B to C
+= Add and then assign the value C += A is equal to C = C + A
-= Subtract and then assign C -= A is equal to C = C - A
*= Multiply and then assign C *= A is equal to C = C * A
/= divide and then assign C /= A is equal to C = C / A
%= Find the remainder and then assign the value C %= A is equal to C = C % A
<<= Assign value after left shift C <<= 2等于 C = C << 2
>>= Assign after shifting right C >>= 2 is equal to C = C >> 2
&= Bitwise AND followed by assignment C &= 2 is equal to C = C & 2
^= Bitwise XOR and then assignment C ^= 2 is equal to C = C ^ 2
|= Bitwise OR followed by assignment C |= 2 is equal to C = C | 2

5. Bit operators

operator describe Example
& bitwise AND operator (a & b) Output result 12, binary interpretation: 0000 1100
| Bitwise OR operator (a | b) The output result is 61, binary interpretation: 0011 1101
^ Bitwise XOR operator (a ^ b) output result 49, binary interpretation: 0011 0001
~ bitwise negation operator (~a) The output result is -61, binary interpretation: 1100 0011, in the two's complement form of a signed binary number.
<< left shift operator a << 2 输出结果 240 ,二进制解释: 0011 0000
>> 右移动运算符 a >> 2 输出结果 15 ,二进制解释: 0000 1111
>>> 无符号右移 a >>>2 输出结果 15, 二进制解释: 0000 1111
var a:Int=1000
// 4000 
println(a << 2)
//  250
println(a >> 2)

6、Scala运算符本质

在Scala中其实是没有运算符的,所有运算符都是方法。

  • 当调用对象的方法时,点.可以省略。
  • 如果函数参数只有一个,或者没有参数,()可以省略。
var nu1: Int = 1.+(1)
val nu2: Int = 1 + (1)
val nu3: Int = 1 + 1

四、流程控制

1、分支控制(if-else)

让程序有选择的的执行,分支控制有三种:单分支、双分支、多分支。

//	1、简单If-else
if (age < 18) {
    
    
  println("童年")
} else if (age > 18 && age < 60) {
    
    
  println("中年")
} else {
    
    
  println("老年")
}

//	2、使用方法计算
val result: String = {
    
    
  if (age < 18) {
    
    
    "童年"
  } else {
    
    
    "老年"
  }
}
println(result)

//	3、不确定返回值类型。使用Any代替
val res: Any = {
    
    
  if (age > 18) {
    
    
    "童年"
  } else {
    
    
    100
  }
}
println(res)

//	4、三元运算符使用
val res01: Any = if (age > 18) "童年" else "中年"
println(res01)

2、For循环控制

for (i <- 0 to 5) {
    
    
  println(i)
}
for (i <- 0 until 5) {
    
    
  println(i)
}
for (i <- 0 to 5) {
    
    
  if (i > 1) {
    
    
    print(i)
  }
}
for (i <- 0 to 5 if i > 2) {
    
    
  print(i)
}
//  (10, 10, 10, 10)
val ints: immutable.IndexedSeq[Int] = for (i <- 0 to 3) yield {
    
    
  10
}

3、while和do…while循环

var i = 0
//	while循环
while (i < 5) {
    
    
  println(i)
  i += i
}
//	do-while循环
do {
    
    
  println(i)
  i += i
} while (i < 5)
4、循环中断

Scala内置控制结构特地去掉了break和continue,是为了更好的适应函数式编程,推荐使用函数式的风格解决break和continue的功能,而不是一个关键字。Scala中使用breakable控制结构来实现break和continue功能。

//	1、采用异常的方式退出循环
try {
    
    
  for (e <- 1 to 5) {
    
    
    println(e)
    if (e > 4) {
    
    
      throw new Exception()
    }
  }
} catch {
    
    
  case e: Throwable =>
}

//	2、采用Scala自带的函数,退出循环
Breaks.breakable(
  for (e <- 1 to 5) {
    
    
    println(e)
    if (e > 4) {
    
    
      Breaks.break()
    }
  }
)

//	3、对break进行省略
breakable {
    
    
  for (e <- 1 to 10) {
    
    
    if (e > 9) {
    
    
      break
    }
  }
}

四、函数式编程

  • 面向对象编程
    解决问题,分解对象,行为,属性,然后通过对象的关系以及行为的调用来解决问题。
    对象:用户;
    行为:登录、连接jdbc、读取数据库
    属性:用户名、密码
    Scala语言是一个完全面向对象编程语言。万物皆对象
  • 函数式编程
    解决问题时,将问题分解成一个一个的步骤,将每个步骤进行封装(函数),通过调用这些封装好的步骤,解决问题。
    例如:请求->用户名、密码->连接jdbc->读取数据库
    Scala语言是一个完全函数式编程语言。万物皆函数

1、方法定义

Insert image description here

object Hello {
    
    
  def main(args: Array[String]): Unit = {
    
    
    //  自定义方法:f:方法名。arg:参数名。String:参数类型。Unit:返回值空
    def f(arg: String): Unit = {
    
    
      println(arg)
    }
    //  调用方法
    f("hello world")
  }
}
  • 方法1:无参,无返回值
  • 方法2:无参,有返回值
  • 方法3:有参,无返回值
  • 方法4:有参,有返回值
  • 方法5:多参,无返回值
//  有参。有返回值
def f1(arg: String): String = {
    
    
  arg + " world"
}
println(f1("hello"))

//  有参。无返回值
def f2(args: String): Unit = {
    
    
  println(args)
}
println(f2("hello"))

//  无参。有返回值
def f3(): String = {
    
    
  "hello world"
}
println(f3())

//  无参。无返回值
def f4(): Unit = {
    
    
  println("hello world")
}
f4()

//  多参。无返回值
def f5(args01: String, args02: String): Unit = {
    
    
  args01 + args02
}
f5("hello", "world")

方法至简原则

  • return可以省略,Scala会使用方法体的最后一行代码作为返回值
  • 如果方法体只有一行代码,可以省略花括号
  • 返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)特别注意事项:
  • 如果有return,则不能省略返回值类型,必须指定
  • 如果方法明确声明unit,那么即使方法体中使用return关键字也不起作用
  • Scala如果期望是无返回值类型,可以省略等号(=号和方法体大括号不能同时省略)
  • 如果方法无参,但是声明了参数列表,那么调用时,小括号,可加可不加(声明无括号调用时也没有括号)
  • 如果方法没有参数列表,那么小括号可以省略,调用时小括号必须省略
//    1、return可以省略,Scala会使用方法体的最后一行代码作为返回值
def s1(): String = {
    
    
  "hello01"
  "hello02"
}
//    2、如果方法体只有一行代码,可以省略花括号
def s2(): Int = 1 + 2
//    3、返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)特别注意事项:
def s3() = 1 + 2
//    4、如果有return,则不能省略返回值类型,必须指定
def s4(): Int = {
    
    
  return 1 + 2
}
//    5、如果方法明确声明unit,那么即使方法体中使用return关键字也不起作用
def s5(): Unit = {
    
    
  return 1 + 2
}
//    6、Scala如果期望是无返回值类型,可以省略等号(=号和方法体大括号不能同时省略)
def s6() {
    
    
  println(1 + 1)
}
//    7、如果方法无参,但是声明了参数列表,那么调用时,小括号,可加可不加(声明无括号调用时也没有括号)
def s7(): Unit = {
    
    
  println(1 + 2)
}
s7()
s7
//    8、如果方法没有参数列表,那么小括号可以省略,调用时小括号必须省略
def s8 {
    
    
  println("hello")
}
s8

2、可变参数

  • 可变参数:本质是1个数组
  • 参数位置:如果参数列表中存在多个参数,那么可变参数一般放置在最后,(不能和默认值一起用,和带名参数用时,不能改变带名参数的顺序)
  • 参数默认值:一般将有默认值的参数放置在参数列表的后面
//  1、可变参数。本质是1个数组
def sayHi(name: String*): Unit = {
    
    
  println(s"hi $name")
  for (e <- name) {
    
    
    println(name)
  }
}
sayHi("hello01", "hello02", "hell03")
//  2、可变参数必须要放在参数列表最后
def sayHi2(age: Int, name: String*): Unit = {
    
    
  println(s"hi $name")
}
sayHi2(18, "hello01", "hello02")
//  3、参数默认值
def sayHi3(age: Int, name: String = "张三"): Unit = {
    
    
  println(s"hi $age $name")
}
//  hi 18 张三
sayHi3(18)
//  4、默认值参数在使用的时候,可以不在最后
def say4(name: String = "张三", age: Int): Unit = {
    
    
  println(s"hi $age $name")
}
sayHi3(18)

3、函数

函数的返回值就是函数体中最后一个表达式的结果值/return语句的返回值。

Insert image description here

函数和方法的区别

  • 方法定义在类中可以实现重载,函数不可以重载。
  • 方法是保存在方法区,函数是保存在堆中。
  • 定义在方法中的方法可以称之为函数,不可以重载。
  • 方法可以转成函数, 转换语法: 方法名 _。
//	不支持重载
val test = () => {
    
    
  println("无参")
}
test()
//    val test =(name:String)=>{
    
    
//      println("构造")
//    }

//	3、函数可以嵌套
val test02 = () => {
    
    
  val test03 = () => {
    
    
    println("无参+构造")
  }
  println("无参")
}
test02()

//	4、方法可以转为函数
def add(): Unit = {
    
    
  println("方法")
}
val add2 = add _

4、高阶函数

参数/返回值为函数的方法/函数称为高阶函数

//  函数ope作为参数传入,
def cal(a: Int, b: Int, ope: (Int, Int) => Int): Int = {
    
    
  ope(a, b)
}
//  函数求和
def plus(x: Int, y: Int): Int = {
    
    
  x + y
}
println(cal(1, 3, plus))

5、匿名函数

没有名字的函数/方法就是匿名函数。

(x:Int)=>{函数体}

x:表示输入参数名称;Int:表示输入参数类型;函数体:表示具体代码逻辑

  • 参数的类型可以省略,会根据形参进行自动的推导

  • 类型省略之后,发现只有一个参数,则圆括号可以省略;其他情况:没有参数和参数超过1的永远不能省略圆括号。

  • 匿名函数如果只有一行,则大括号也可以省略

  • 如果参数只出现一次,且按照顺序出现则参数省略且后面参数可以用_代替

val f0: (Int, Int) => Int = (x: Int, y: Int) => x + y
//  1、参数的类型可以省略,会根据形参自动推导
val f1: (Int, Int) => Int = (x, y) => x + y
//2、类型省略后,发现只有1个参数,则圆括号可以省略
val f2: (Int, Int) => Int = (x, y) => x + y
val f3: Int => Int = x => x + 22
val f4: () => Int = () => 22
//  3、匿名函数如果只有一行,则大括号也可以省略
val f5: (Int, Int) => Int = (x, y) => {
    
    
  x + y
}
val f6: (Int, Int) => Int = (x, y) => x + y
//  4、如果参数只出现一次,且按照顺序出现,则参数省略且后面的参数可以用_代替
val f7: (Int, Int) => Int = _ + _

不能化简为下划线的情况: 1.化简之后只有一个下划线 2.化简后的函数存在嵌套

//  1、传入的参数类型可以推断 所以可以省略
val f8: (Int, Int) => Int = (x, y) => y - x
val f8_1: (Int, Int) => Int = _ - _
//  2、参数必须只使用一次,使用的顺序必要和定义的顺序一直
val f9: (Int, Int) => Int = -_ + _

6、函数柯里化&闭包

**函数柯里化:**将一个接收多个参数的函数转化成一个接受一个参数的函数过程,可以简单的理解为一种特殊的参数列表声明方式。
**闭包:**就是一个函数和与其相关的引用环境(变量)组合的一个整体(实体)

//  外部参数
var z: Int = 10
//  闭包
def f(y: Int): Int = {
    
    
  z + y
}

1

//	原样
val sum = (x: Int, y: Int, z: Int) => x + y + z
sum(1, 2, 3)
//	1、
val sum1 = (x: Int) => {
    
    
  y: Int => {
    
    
    z: Int => {
    
    
      x + y + z
    }
  }
}
sum1(1)(2)(3)

//	2、第二种
val sum2 = (x: Int) => (y: Int) => (z: Int) => x + y + z
sum2(1)(2)(3)
//	3、第三种
def sum3(x: Int)(y: Int)(z: Int) = x + y + z
sum3(1)(2)(3)

7、递归

一个函数/方法在函数/方法体内又调用了本身,我们称之为递归调用

def main(args: Array[String]): Unit = {
    
    
  println(test(5))
}
//	递归方法(test方法)
def test(i: Int): Int = {
    
    
  if (i == 1) {
    
    
    1
  } else {
    
    
    i * test(i - 1)
  }
}

五、面向对象

1、定义

  • Scala语法中,类并不声明为public,所有这些类都具有公有可见性(即默认就是public)。
  • 一个Scala源文件可以包含多个类。

基本语法

[修饰符] class 类名 {
    
    
    类体
} 
package com.atguigu.chapter06

//(1)Scala语法中,类并不声明为public,所有这些类都具有公有可见性(即默认就是public)
class Person {
    
    

}

//(2)一个Scala源文件可以包含多个类
class Teacher{
    
    

}

2、属性和封装

class Person {
    
    
  @BeanProperty
  var age: Int = _

  //  val只能生成get方法
  @BeanProperty
  val name: String = "张三"

}

测试类

object Test {
    
    
  def main(args: Array[String]): Unit = {
    
    
    val person = new Person

    person.age = 18
    person.setAge(18)
    println(person.getName)
  }
}

3、访问权限

  • Scala 中属性和方法的默认访问权限为public,但Scala中无public关键字。
  • private为私有权限,只在类的内部和伴生对象中可用。
  • protected为受保护权限,Scala中受保护权限比Java中更严格,同类、子类可以访问,同包无法访问。
  • private[包名]增加包访问权限,包名下的其他类也可以使用

4、方法

def 方法名(参数列表) [:返回值类型] = {
    
     
	方法体
}

案例

class Person {
    
    
  def sum(n1: Int, n2: Int): Int = {
    
    
    n1 + n2
  }
}

object Test01 {
    
    
  def main(args: Array[String]): Unit = {
    
    
    val test: Test01 = new Test01()
    println(test.sum(1, 2))
  }
}

5、构造器

和Java一样,Scala构造对象也需要调用构造方法,并且可以有任意多个构造方法。

Scala类的构造器包括:主构造器和辅助构造器

class 类名(形参列表) {
    
      // 主构造器
   // 类体
   def  this(形参列表) {
    
      // 辅助构造器
   }
   def  this(形参列表) {
    
      //辅助构造器可以有多个...
   }
} 

案例:

//  主构造器
class Person01(name: String) {
    
    
  val name1: String = name
  var age: Int = _

  //  辅助构造器01
  def this() = {
    
    
    this("张三")
  }

  //  复制构造器02
  def this(name: String, age1: Int) = {
    
    
    this()
    this.age = age1
  }
}

测试类

object Test {
    
    
  def main(args: Array[String]): Unit = {
    
    
    val person01: Person01 = new Person01()
    val person02: Person01 = new Person01("张三")
    val person03: Person01 = new Person01("张三", 18)
  }
}

6、构造器参数

Scala类的主构造器函数的形参包括三种类型:未用任何修饰、var修饰、val修饰

  • 未用任何修饰符修饰:这个参数就是一个局部变量,底层有属性的特性。
  • var修饰参数:作为类的成员属性使用,可以修改。
  • val修饰参数:作为类只读属性使用,不能修改。
//  主构造器参数 分为3类:
//  没有修饰符: 作为构造方法中的传入参数使用
//  val 修饰: 会自动生产同名的属性 并且定义为val
//  var 修饰 : 会自动生产同名的属性 并且定义为var
class Person02(name1: String, var age1: Int, val sex1: Int) {
    
    
  val name: String = name1
}

//	等效方法
class Person02(name1: String, age1: Int,sex1 : Int) {
    
    
  val name: String = name1
  val age: Int = age1
  val sex: Int = sex1
}

7、scala的object

java中存在静态属性、静态方法、非静态属性、非静态方法。

  • scala当中不存在静态与非静态。object中定义的所有属性与方法、函数,除开private修饰的,都可以通过对象名.属性、对象名.方法、对象名.函数 的方式调用,可以理解为java中的static修饰的。
  • Scala语言是完全面向对象的语言,所以并没有静态的操作(即在Scala中没有静态的概念)。但是为了能够和Java语言交互(因为Java中有静态概念),就产生了一种特殊的对象来模拟类对象,该对象为单例对象。若单例对象名与类名一致,则称该单例对象这个类的伴生对象,这个类的所有“静态”内容都可以放置在它的伴生对象中声明。
object TestObject {
    
    
  val name: String = "zhansan"
  var age: Int = 30

  private val address: String = "深圳"

  def getName(): String = {
    
    
    this.name + " " + this.name
  }

  val func = (x: Int, y: Int) => {
    
    
    x * y
  }
}
object Test102 {
    
    

  def main(args: Array[String]): Unit = {
    
    
    //  object中的属性直接通过 类名.属性名 方式调用(zhansan)
    println(TestObject.name)
    //  30
    println(TestObject.age)
    //  设置属性
    TestObject.age = 66
    //  66
    println(TestObject.age)
    println("====")
    //  object中的方法直接通过 类名.方法名 方式调用(zhansan zhansan)
    println(TestObject.getName())
    //  object中的方法直接通过 类名.函数名 方式调用(6)
    println(TestObject.func(2, 3))
  }
}

8、伴生类与伴生对象

  • 如果有一个class,另外还有一个object,并且二者同名。
  • class与object在同一个文件中。

如果满足上两个条件,那么就称这个object为class的伴生对象,称class为object的伴生类。

  • 伴生类与伴生对象可以互相访问对方的私有成员。
  • 伴生类可以互相访问private修饰的值,但是在外部不能够访问。
class ClassObjectTest {
    
    
  
  val name:String = "lisi"
  //用private修饰的只能在类或者伴生对象中使用
  private val age = 20
  //此时可以调用伴生对象中用private修饰的 address属性
  def getAddress() = ClassObjectTest.address
}

object ClassObjectTest{
    
    
  
  private val address = "shenzhen"

  def getName() = {
    
    
    //创建伴生类的对象
	val obj = new ClassObjectTest()
	//此时可以调用伴生类中用private修饰的name属性
	obj.name
}
}

9、apply方法

  • 通过伴生对象的apply方法,实现不使用new方法创建对象。
  • 如果想让主构造器变成私有的,可以在()之前加上private。
  • apply方法可以重载。
  • Scala中obj(arg)的语句实际是在调用该对象的apply方法,即obj.apply(arg)。用以统一面向对象编程和函数式编程的风格。
  • 当使用new关键字构建对象时,调用的其实是类的构造方法,当直接使用类名构建对象时,调用的其实时伴生对象的apply方法。
object Test11_Apply {
    
    

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

    // 如果调用的方法是apply的话  方法名apply可以不写
    val one: Person11 = Person11()
    // 类的apply方法调用(打印类方法)
    one()
  }
}

class Person11 private() {
    
    
  var name:String = _
  def this(name:String){
    
    
    this()
    this.name = name
  }

  def apply(): Unit = println("类的apply方法调用")
}

object Person11 {
    
    
  // 使用伴生对象的方法来获取对象实例
  def getPerson11: Person11 = new Person11

  // 伴生对象的apply方法
  def apply(): Person11 = new Person11()

  // apply方法的重载
  def apply(name: String): Person11 = new Person11(name)
 }
}

10、类型检查和转换

//	判断obj是不是T类型。
obj.isInstanceOf[T]
//	将obj强转成T类型。
obj.asInstanceOf[T]
//	获取类模板。
classOf
// 	判断person类型是不是为Teacher
if (person1.isInstanceOf[Teacher]) {
    
    
  //	假如类型为Teacher,则强转为Teacher
  val teacher1: Teacher = person1.asInstanceOf[Teacher]
  teacher1.sayHi1()
}
// 调用固定的方法  返回类模板
val value: Class[Student15] = classOf[Student15]

六、集合

1、简介

  • Scala的集合有三大类:序列Seq(List)、集Set、映射Map,所有的集合都扩展自Iterable特质。

  • 对于几乎所有的集合类,Scala都同时提供了可变不可变的版本,分别位于以下两个包。

    • 不可变集合:scala.collection.immutable
    • 可变集合: scala.collection.mutable
  • Scala不可变集合,就是指该集合对象不可修改,每次修改就会返回一个新对象,而不会对原对象进行修改。类似于java中的String对象。

  • 可变集合,就是这个集合可以直接对原对象进行修改,而不会返回新的对象。类似于java中StringBuilder对象。

建议:在操作集合的时候,不可变用符号,可变用方法。

不可变集合

Insert image description here

  • Set、Map是Java中也有的集合。
  • Seq是Java没有的,我们发现List归属到Seq了,因此这里的List就和Java不是同一个概念了。.
  • 我们前面的for循环有一个 1 to 3,就是IndexedSeq下的Range。
  • String也是属于IndexedSeq。
  • 我们发现经典的数据结构比如Queue和Stack被归属到LinearSeq(线性序列)。
  • 大家注意Scala中的Map体系有一个SortedMap,说明Scala的Map可以支持排序。
  • IndexedSeq和LinearSeq的区别。
    • IndexedSeq是通过索引来查找和定位,因此速度快,比如String就是一个索引集合,通过索引即可定位。
    • LinearSeq是线型的,即有头尾的概念,这种数据结构一般是通过遍历来查找。

可变集合

Insert image description here

2、数组

2.1 不可变数组
val arr1 = new Array[Int](10)
  • new是关键字。
  • [Int]是指定可以存放的数据类型,如果希望存放任意数据类型,则指定Any。
  • (10),表示数组的大小,确定后就不可以变化。
object Test01 {
    
    
  def main(args: Array[String]): Unit = {
    
    

    //  创建不可变数组
    val ints = new Array[Int](10)
    //  可以使用伴生对象的apply方法
    val array01: Array[Int] = Array(1, 2, 3, 4, 5)

    //  遍历数组(print打印)
    println(array01.toList)
    //  遍历数组(遍历)
    for (e <- array01) {
    
    
      println(e)
    }
    //  遍历数组(迭代器)
    val iterator: Iterator[Int] = array01.iterator
    while (iterator.hasNext) {
    
    
      println(iterator.next())
    }
    //  遍历数组(匿名函数)
    array01.foreach(e => println(e))
    //  遍历数组(使用系统函数)
    array01.foreach(println)
    
    //  修改数组值
    array01(0) = 10
    println(array01(0))

    // 添加元素,生成新数组array02、原数组array01不变
    val array02: Array[Int] = array01 :+ 1
  }
}
2.2 可变数组

定义:

  • [Any]存放任意数据类型。
  • (3, 2, 5)初始化好的三个元素。
  • ArrayBuffer需要引入scala.collection.mutable.ArrayBuffer
 val array: ArrayBuffer[Int] = ArrayBuffer(1, 2, 3)

ArrayBuffer是有序的集合。

增加元素使用的是append方法(),支持可变参数。

//  创建可变数组
val array: ArrayBuffer[Int] = new ArrayBuffer[Int]()
val arrayBuffer02: ArrayBuffer[Int] = ArrayBuffer(1, 2, 3, 4)
//  向可变数组中添加元素
array.append(10)
array.appendAll(Array(1, 2, 3, 4))
//  循环打印
array.foreach(println)
println(array)
//  更新元素值
array.update(0, 100)
array(1) = 200
println(array)
println(array(0))
//  删除元素
array.remove(0)
array.remove(1, 3)
2.3 不可变数组与可变数组的转换
  • arr2.toArray返回结果才是一个不可变数组,arr2本身没有变化。
  • arr1.toBuffer返回结果才是一个可变数组,arr1本身没有变化。
//	不可变数组转可变数组
arr1.toBuffer
//	不可变数组转可变数组
arr2.toArray
//  不可变数组
val array: Array[Int] = Array(1, 2, 3, 4)
//  可变数组
val arrayBuffer: ArrayBuffer[Int] = ArrayBuffer(5, 6, 7, 8)
//  添加元素(不可变数组)
val arra01: Array[Int] = array :+ 1
arrayBuffer.append(1)
//  可变 => 不可变
val array01: Array[Int] = arrayBuffer.toArray
//  不可变 => 可变
val buffer: mutable.Buffer[Int] = array.toBuffer
val arrayBuffer01: ArrayBuffer[Int] = buffer.asInstanceOf[ArrayBuffer[Int]]
2.4 多维数组
  • 二维数组中有三个一维数组,每个一维数组中有四个元素。
//	3行4列
val array02: Array[Array[Double]] = Array.ofDim[Double](3, 4)
val arrayDim: Array[Array[Double]] = Array.ofDim[Double](3, 4)
//  赋值
arrayDim(0) = Array(1, 2, 3, 4)
arrayDim(0)(1) = 100
//	遍历
for (array <- arrayDim) {
    
    
  for (elem <- array) {
    
    
    print(elem + " ")
  }
}

3、Seq集合(List)

不可变List

//  创建集合(List)
val list01: List[Any] = List(1, 1, 1, 1.0, "hello", 'c')
val list02: List[Int] = List(1, 2, 3, 4)
//  遍历集合
list01.foreach(println)

//  增加数据(末尾增加)
val list011: List[Any] = list01 :+ 1
//  增加数据(开头增加)
val list012: List[Any] = 2 :: list011

//  合并2个集合(1个集合插入另1个集合中)(List(List(1, 1, 1, 1.0, hello, c), 1, 2, 3, 4))
val list013: List[Any] = list01 :: list02
//  合并2个集合(1个集合元素遍历插入另1集合)(List(1, 1, 1, 1.0, hello, c, 1, 2, 3, 4))
println(list01 ::: list02)

//  取值
val value: Int = list02(2)

// 空集合Nil(List(1, 2, 3, 4))
val ints: List[Int] = 1 :: 2 :: 3 :: 4 :: Nil

可变ListBuffer

//  可变List创建(ListBuffer())
val listBuffer: ListBuffer[Int] = new ListBuffer[Int]()
//  ListBuffer(1, 2, 3, 4)
val listBuffer1: ListBuffer[Int] = ListBuffer(1, 2, 3, 4)

//  添加元素(末尾头)(ListBuffer(5))
listBuffer.append(5)
//  添加元素(开头)(ListBuffer(0, 5))
listBuffer.prepend(0)

//  删除元素(ListBuffer(5))
listBuffer.remove(0)
//  更新值(ListBuffer(5))
listBuffer(0)=1

4、Set集合

默认情况下,Scala使用的是不可变集合,如果你想使用可变集合,需要引用 scala.collection.mutable.Set包。

不可变集合

  • Set默认是不可变集合。
  • 数据无序不可重复。
  • 默认使用hash set
val set: Set[Int] = Set(3, 2, 4, 1)
// set的特点 无序不可重复
println(set)
// 不可变使用符号(无变化s)(Set(3, 2, 4, 1))
val set2: Set[Int] = set + 1
println(set)
println(set2)
// 作用 判断集合是否包含某个元素(true)
val bool: Boolean = set.contains(2)
println(bool)

可变集合mutable.Set

//  创建可变Set(Set(1, 5, 2, 3, 4))
val set: mutable.Set[Int] = mutable.Set(1, 2, 3, 4, 5)
//  添加数据(1, 5, 2, 3, 4)
val bool: Boolean = set.add(5)
//  遍历
set.foreach(println)
//  删除元素(Set(1, 5, 3, 4))
val bool1: Boolean = set.remove(2)
println(set)

5、Map集合

Scala中的Map和Java类似,**也是一个散列表,它存储的内容也是键值对(key-value)**映射。

不可变Map

//  1、创建不可变Map
val map: Map[String, Int] = Map("hello" -> 100, "world" -> 200)
val map02: Map[String, Int] = Map(("hello", 100), ("world", 200))

//  2、遍历打印
for (elem <- map) {
    
    
  println(elem)
}
//  遍历打印(foreach打印)
map.foreach(print)
//  遍历打印(打印key)
val keys: Iterable[String] = map.keys
keys.foreach(print)
//  遍历打印(打印value)
val values: Iterable[Int] = map.values
//  遍历打印(直接打印)
println(map)

//  3-1、获取value的值(Option)
val option: Option[Int] = map.get("hello")
//  根据Option取值(option.isDefined/option.isEmpty)
if (!option.isEmpty) {
    
    
  println(option.get)
}
println(option.getOrElse(1))
//  3-1、获取value的值(getOrElse)
val map01: Map[Int, String] = Map((1, "4324"))
//  不确定存在
val str1: String = map01.getOrElse(1, "4324")
//  按key取值
val str: String = map01(1)

可变Map

//  创建可变map
val map: mutable.Map[String, Int] = mutable.Map(("a", 1), ("b", 1), ("c", 2), ("d", 4))
println(map)

//  添加元素
map.put("z", 10)
println(map)
//  修改元素值
map.update("b", 20)
map("a") = 30
//  删除元素
map.remove("a")

6、元组

  • 元组也是可以理解为一个容器,可以存放各种相同或不同类型的数据。说的简单点,就是将多个无关的数据封装为一个整体,称为元组。
  • 注意:元组中最大只能有22个元素。
//  1、声明元组
val tuple: (Int, String, Boolean) = (40, "bobo", true)
//  2-1、访问元组(单个)
println(tuple._1)
//  2-2、访问元组(单个)
val value: Any = tuple.productElement(0)
println(value)
//  2-3、访问元组(迭代器遍历)
for (elem <- tuple.productIterator) {
    
    
  println(elem)
}
//	3、Map中使用(以元组方式初始化Map)
val map: Map[String, Int] = Map("a" -> 1, "b" -> 2, "c" -> 3)
val map02: Map[String, Int] = Map(("a", 1), ("b", 2), ("c", 3))

7、集合常用函数

7.1 基本操作
  • 获取集合长度
  • 获取集合大小
  • 循环遍历
  • 迭代器
  • 生成字符串
  • 是否包含
val list: List[Int] = List(1, 2, 3, 4, 5, 6)
//  获取集合长度
println(list.length)
//  获取集合的大小(size=length)
println(list.size)
//  循环遍历
list.foreach(println)
//  迭代器
for (elem <- list.iterator) {
    
    
  println(elem)
}
//  按规定字符串(1,2,3,4,5,6)
println(list.mkString(","))
//  是否包含(true)
println(list.contains(3))
7.2 衍生集合
  • 获取集合的头
  • 获取集合的尾(不是头的就是尾)
  • 集合最后一个数据
  • 集合初始数据(不包含最后一个)
  • 反转
  • 取前(后)n个元素
  • 去掉前(后)n个元素
  • 并集
  • 交集
  • 差集
  • 拉链
  • 滑窗
val list: List[Int] = List(1, 2, 3)
val list02: List[Int] = List(3,4, 5)
//  获取集合的头(1)
println(list.head)
//  获取集合最后1个元素(3)
println(list.last)
//  集合头数据(不包含最后1个)(List(1, 2))
println(list.init)
//  获取集合的尾(不包含第1个)(List(2, 3))
println(list.tail)
//  反转(List(3, 2, 1))
println(list.reverse)
//  取前(后)n个元素
println(list.take(2))
println(list.takeRight(2))

//  去掉前(后)n个元素(List(3))(List(1))
println(list.drop(2))
println(list.dropRight(2))
//  并集(List(1, 2, 3, 4, 5))
println(list.union(list02))
//  交集(List(3))
println(list.intersect(list02))
//  差集(List(1, 2))
println(list.diff(list02))
//  拉链(List((1,3), (2,4), (3,5)))
println(list.zip(list02))

//  划窗(List(1, 2))
list.sliding(2, 5).foreach(println)
7.3 集合计算初级函数
  • 求和
  • 求乘积
  • 最大值
  • 最小值
  • 排序
    • sorted:对一个集合进行自然排序,通过传递隐式的Ordering。
    • sortBy:对一个属性或多个属性进行排序,通过它的类型。
    • sortWith:基于函数的排序,通过一个comparator函数,实现自定义排序的逻辑。
val list: List[Int] = List(1, 5, -3, 4, 2, -7, 6)
val list1: ListBuffer[Int] = ListBuffer(1, 5, -3, 4, 2, -7, 6)
//  1、求和
println(list.sum)
//  2、求乘积
println(list.product)
//  3、最大值
println(list.max)
//  4、最小值
println(list.min)
//  5-1、排序(默认)(从小到大)
println(list.sorted)
//  排序(从大到小)
println(list.sorted(Ordering[Int].reverse))
//  5-2、排序(元组)
val tuples: List[(String, Int)] = List(("hello", 10), ("world", 12), ("scala", 9))
//  排序(元组)(默认字典序)
println(tuples.sorted)
//  排序(元组)(第2个元素从小到大排序)
val tuples1: List[(String, Int)] = tuples.sortBy(one => one._2)
tuples.sortBy(_._2)
//  排序(元组)(第2个元素从大到小排序)
println(tuples.sortBy((one => one._2))(Ordering[Int].reverse))
//  5-3、自定义排序规则
val tuples2: List[(String, Int)] = tuples.sortWith((left: (String, Int), right: (String, Int)) => {
    
    
  //  自定义规则
  left._2 > right._2
})
tuples.sortWith((a, b) => a._2 > b._2)
tuples.sortWith(_._2 > _._2)
7.4 高级函数
  • **过滤:**遍历一个集合并从中获取满足指定条件的元素组成一个新的集合。

  • **转化/映射(map):**将集合中的每一个元素映射到某一个函数。

  • 扁平化

  • **扁平化+映射:**注:flatMap相当于先进行map操作,在进行flatten操作。集合中的每个元素的子元素映射到某个函数并返回新集合。

  • **分组(groupBy):**按照指定的规则对集合的元素进行分组。

  • 简化(归约)

  • 折叠

val list: List[Int] = List(1, 2, 3, 4, 5)
val nestedList: List[List[Int]] = List(List(1, 2, 3), List(4, 5, 6))
val wordList: List[String] = List("hello world", "hello scala")
//  1、过滤(List(2, 4))
println(list.filter(x => x % 2 == 0))
//  2、转化、映射(List(2, 3, 4, 5, 6))
println(list.map(x => x + 1))
//  3、扁平化(List(1, 2, 3, 4, 5, 6))
println(nestedList.flatten)
//  4、扁平化+映射(List(hello, world, hello, scala))
println(wordList.flatMap(x => x.split(" ")))
//  5、分组(Map(1 -> List(1, 3, 5), 0 -> List(2, 4)))
println(list.groupBy(x => x % 2))
  • Reduce简化(归约) :通过指定的逻辑将集合中的数据进行聚合,从而减少数据,最终获取结果。
val list: List[Int] = List(1, 2, 3, 4)
//  将数据两两结合,实现运算规则(1-2-3-4 = -8)
println(list.reduce((x, y) => x - y))
//  从源码的角度,reduce底层调用的其实就是reduceLeft(1-2-3-4 = -8)
println(list.reduceLeft((x, y) => x - y))
//  (((4-3)-2-1) = -2)
println(list.reduceRight((x, y) => x - y))
  • Fold折叠:化简的一种特殊情况,可以添加初始值
val list: List[Int] = List(1, 2, 3, 4)
// fold方法使用了函数柯里化,存在两个参数列表
// 第一个参数列表为 : 零值(初始值)
// 第二个参数列表为: 简化规则
// fold底层其实为foldLeft
println(list.foldLeft(1)((x, y) => x - y))
println(list.foldRight(10)((x, y) => x - y))

8、队列

Scala也提供了队列(Queue)的数据结构,队列的特点就是先进先出。进队和出队的方法分别为enqueuedequeue

//	初始化对接
val que: mutable.Queue[String] = new mutable.Queue[String]()
//	插入元素(Queue(a, b, c))
que.enqueue("a","b","c")
//  弹出元素(a)
println(que.dequeue())

七、模式匹配

1、基本语法

Scala中的模式匹配类似于Java中的switch语法

模式匹配语法中,采用match关键字声明,每个分支采用case关键字进行声明,当需要匹配时,会从第一个case分支开始,如果匹配成功,那么执行对应的逻辑代码,如果匹配不成功,继续执行下一个分支进行判断。如果所有case都不匹配,那么会执行case _分支,类似于Java中default语句。

  • 如果所有case都不匹配,那么会执行case _ 分支,类似于Java中default语句,若此时没有case _ 分支,那么会抛出MatchError。
  • 每个case中,不需要使用break语句,自动中断case。
  • match case语句可以匹配任何类型,而不只是字面量。
  • => 后面的代码块,直到下一个case语句之前的代码是作为一个整体执行,可以使用{}括起来,也可以不括。
val num: Int = 3
val result: String = num match {
    
    
  case 1 => "1"
  case 2 => "2"
  case 3 => "3"
  case _ => "未匹配上"
}
//  3
println(result)

2、常见匹配用法

匹配类型

需要进行类型判断时,可以使用前文所学的isInstanceOf[T]asInstanceOf[T],也可使用模式匹配实现同样的功能。

def func(x: Any): String = {
    
    
  x match {
    
    
    case a: Int => "整数"
    case b: Char => "字符"
    case c: String => "字符串"
    case _ => "其它"
  }
}
println(func(1))
println(func('\t'))
println(func("1232"))

执行结果:

整数
字符
字符串

匹配元组

for (tuple <- Array((0, 1), (1, 0), (1, 1), (1, 0, 2), (1, 2, 3, 4))) {
    
    
  val result: String = tuple match {
    
    
    //  匹配0开头
    case (0, _) => "0 ..."
    //  匹配2个值
    case (y, _) => "" + y + "0"
    //  匹配3个值
    case (a, b, c) => "" + a + " " + b + " " + c
    case _ => "other"
  }
  println(result)
}

执行结果

0 ...
10
10
1 0 2
other

匹配对象

案例:

object Test05 {
    
    
  def main(args: Array[String]): Unit = {
    
    
    val person: Person5 = new Person5("张三", 18)

    person match {
    
    
      case Person5("张三", 18) => println("找到张三了")
      case _ => println("你不是张三")
    }
  }
}
class Person05 (val name:String,var age:Int){
    
    

}

object Person05{
    
    
  // 创建对象的方法
  def apply(name: String, age: Int): Person05 = new Person05(name, age)

  // 解析对象的方法
  def unapply(arg: Person05): Option[(String, Int)] = {
    
    
    // 如果解析的参数为null
    if (arg == null ) None else Some((arg.name,arg.age))
  }
}
  • val user = Person05("zhangsan",11),该语句在执行时,实际调用的是Person05伴生对象中的apply方法,因此不用new关键字就能构造出相应的对象。

  • 若只提取对象的一个属性,则提取器为unapply(obj:Obj):Option[T]

    若提取对象的多个属性,则提取器为unapply(obj:Obj):Option[(T1,T2,T3…)]

    若提取对象的可变个属性,则提取器为unapplySeq(obj:Obj):Option[Seq[T]]

匹配样例类

case class Person05 (name: String, age: Int)
case class Person05(var name: String, age: Int)
  • 样例类仍然是类,和普通类相比,只是其自动生成了伴生对象,并且伴生对象中自动提供了一些常用的方法,如applyunapplytoStringequalshashCodecopy
  • 样例类是为模式匹配而优化的类,因为其默认提供了unapply方法,因此,样例类可以直接使用模式匹配,而无需自己实现unapply方法。
  • 构造器中的每一个参数都成为val,除非它被显式地声明为var(不建议这样做)

3、偏函数中的模式匹配

偏函数也是函数的一种,通过偏函数我们可以方便的对输入参数做更精确的检查。例如该偏函数的输入类型为List[Int],而我们需要的是第一个元素是0的集合,这就是通过模式匹配实现的

Insert image description here

//	返回输入的List集合的第二个元素。
val second: PartialFunction[List[Int], Option[Int]] = {
    
    
  case x :: y :: _ => Some(y)
}

上述代码会被scala编译器翻译成以下代码,与普通函数相比,只是多了一个用于参数检查的函数——isDefinedAt,其返回值类型为Boolean。

val second = new PartialFunction[List[Int], Option[Int]] {
    
    

    //检查输入参数是否合格
    override def isDefinedAt(list: List[Int]): Boolean = list match {
    
    
        case x :: y :: _ => true
        case _ => false
    }

    //执行函数逻辑
    override def apply(list: List[Int]): Option[Int] = list match {
    
    
        case x :: y :: _ => Some(y)
    }
}

偏函数调用

偏函数不能像second(List(1,2,3))这样直接使用,因为这样会直接调用apply方法,而应该调用applyOrElse方法

second.applyOrElse(List(1,2,3), (_: List[Int]) => None)
val list: List[Any] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, "test")
//  1、filter中使用
val list1: List[Any] = list.filter(a => {
    
    
  a match {
    
    
    case s: String => false
    case i: Int => true
  }
})
//	2、map中使用
val list2: List[Int] = list1.map {
    
    
  case i: Int => i + 1
}
//	3、collect中使用
val list3: List[Int] = list.collect({
    
    
  case i: Int => i + 1
})
//	4、PartialFunction 偏函数中使用
val value: PartialFunction[Any, Int] = {
    
    
  case i: Int => i + 1
}
//	5、偏函数中使用
val function111: Any => Int = (a: Any) => a match {
    
    
  case i: Int => i + 1
}
//	6、isInstanceOf中使用
List(1, 2, 3, 4, 5, 6, "test").filter(_.isInstanceOf[Int]).map(_.asInstanceOf[Int] + 1).foreach(println)
//	7、collect中使用
List(1, 2, 3, 4, 5, 6, "test").collect {
    
     case x: Int => x + 1 }.foreach(println)

4、下划线使用总结

  • 1、用于类中的var属性,使用默认值。
  • 2、用于高阶函数的第一种用法,表示函数自身。
  • 3、匿名函数化简,用下划线代替变量。
  • 4、用于导包下的所有内容。
  • 5、用于起别名时表示匿名。
  • 6、用于模式匹配表示任意数据。
class Person001 {
    
    
  //  1、用于类中的var属性,使用默认值。
  var name1: String = _
}
//  2、用于高阶函数的第一种用法,表示函数自身。
def sayHi(name: String): Unit = {
    
    
  println(s"hi $name")
}
val function: String => Unit = sayHi _
//  3、匿名函数化简,用下划线代替变量。
val function01: (Int, Int) => Int = (a: Int, b: Int) => a + b
val function02: (Int, Int) => Int = _ + _
//  4、用于导包下的所有内容。
import scala.util.control.Breaks._
//  5、用于起别名时表示匿名。
import scala.util.control.{
    
    Breaks => _}
//   a / Breaks
//  6、用于模式匹配表示任意数据。
10 match {
    
    
  case 10 => "10"
  case _ => "other"
}

八、异常

  • Scala没有“checked(编译期)”异常,即Scala没有编译异常这个概念,异常都是在运行的时候捕获处理。
  • 如果有异常发生,catch子句是按次序捕捉的。
  • finally子句用于执行不管是正常处理还是有异常发生时都需要执行的步骤,一般用于对象的清理工作。
  • Use the throw keyword to throw an exception object. All exceptions are subtypes of Throwable. The throw expression has a type, which is Nothing. Because Nothing is a subtype of all types, the throw expression can be used where a type is required.
def test():Nothing = {
    
    
    throw new Exception("不对")
}
  • Java provides the throws keyword to declare exceptions. Exceptions can be declared using method definitions. It provides the caller function with information that this method may throw this exception. It helps to call function processing and include that code in a try-catch block to avoid abnormal termination of the program. In Scala, exceptions can be declared using the throws annotation.
def main(args: Array[String]): Unit = {
    
    
  	f11()
}

@throws(classOf[NumberFormatException])
def f11()={
    
    
  	"abc".toInt
}

Complete code:

def main(args: Array[String]): Unit = {
    
    
  try {
    
    
    val n = 10 / 0
  } catch {
    
    
    case ex: ArithmeticException => {
    
    
      println("发生算数异常")
    }
    //	抛出异常
    case ex: NullPointerException => {
    
    
      throw NullPointerException
    }
    case ex: Exception => {
    
    
      println("发生了其他异常")
    }
  } finally {
    
    
    println("finally")
  }
}

Results of the

发生算数异常
finally

9. Implicit conversion

When the compiler fails to compile for the first time, it will look for methods in the current environment that can allow the code to be compiled successfully, which can be used to convert types, achieve secondary compilation, and methods for extending classes.

Implicit conversion can extend the functionality of a class without changing any code.

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

    // 隐式函数
    // 将当前作用域下所有传入参数的类型   隐式转换为  返回值类型
    implicit def changeInt(self: Int) = {
    
    
      new MyRichInt(self)
    }

    val i: Int = 10

    // 比较自身和传入参数的大小  返回较大的值
    val value: Int = i.myMax(20)
    println(value)


    val i1: Int = i << 2
    println(i1)

  }


  // 隐式转换的目标
  class MyRichInt(val self: Int) {
    
    

    def myMax(i: Int): Int = {
    
    
      if (i > self) i else self
    }

    // 如果隐式转换和自身的方法冲突  会使用它自身的  因为不会编译失败
    def <<(x: Int): Int = {
    
    
      0
    }
  }
}

Guess you like

Origin blog.csdn.net/weixin_44624117/article/details/133219900