Scala的函数
1.方法
1.1 语法格式
def 方法名(参数名:参数类型, 参数名:参数类型) : [return type] = {
//方法体
}
注意:
- 参数列表的参数类型不能省略
- 返回值类型可以省略,由scala编译器自动推断
- 返回值可以不写return,默认就是{}块表达式的值
示例:
定义一个方法getMax,用来获取两个整型数字的最大值,并返回结果。
//写法一:普通版
def getMax(a:Int, b:Int):Int = {
return if(a >= b) a else b
}
//写法二:精简版
//省略的地方:1.返回值的类型;2.返回值不写return;3.因为方法体只有一行代码,大括号也可以去掉
def getMax(a:Int, b:Int) = if(a >= b) a else b
//调用
val max = getMax(22, 11)
println(max) //22
1.2 返回值类型判断
Scala定义方法可以省略返回值的数据类型,由scala自动推断。但当定义递归方法时,不能省略返回值类型。
示例:
定义递归方法,求阶乘
def factorial(n:Int):Int = if(n == 1) 1 else n * factorial(n - 1)
如果省略了呢?
1.3 惰性方法
当记录方法返回值的变量被声明为 lazy 时,方法的执行将被推迟,直到我们再次使用该值时方法才会执行,像这样的方法就叫做惰性方法。
注意:
- Java中并没有提供原生态的“惰性”技术,但是可以通过特定的代码结构实现。这种结构称之为:懒加载/延迟加载
- lazy不能修饰var类型的变量
使用场景:
- 打开数据库连接 - 由于表达式执行代价昂贵,因此我们希望能推迟该操作,直到我们确实需要表达式结果值时才执行它
- 提升某些特定模块的启动时间 - 为了缩短模块的启动时间,可以将当前不需要的某些工作推迟执行
- 确保对象中的某些字段优先初始化 - 为了确保对象中的某些字段能优先初始化,我们需要对其他字段进行惰性化处理
示例:
定义一个方法用来获取两个整数和,通过“惰性”技术调用该方法,然后打印结果。
def getSum(a:Int, b:Int) = a + b
val sum1 = getSum(10, 20) //sum1: Int = 30
lazy val sum2 = getSum(10, 20) //sum2: Int = <lazy>
println(sum2) //结果为: 3
1.4 方法参数
1.4.1 默认参数
在定义方法时可以给参数定义一个默认值。
示例:
定义一个计算两个整数之和的方法,两个值默认是10和20;调用该方法,不传任何参数。
def getSum(x:Int = 10, y:Int = 20) = x + y
val sum = getSum()
println(sum)
1.4.2 带名参数
在调用方法时,可以指定参数的名称来进行调用。
示例:
定义一个计算两个整数之和的方法,两个值默认是10和20;调用该方法,只设置第一个参数的值。
def getSum(x:Int = 10, y:Int = 20) = x + y
val sum = getSum(x=1)
println(sum)
1.4.3 变长参数
如果方法的参数时不固定的,可以将该方法的参数定义成变长参数。
格式:
def 方法名(参数名:参数类型*):返回值类型 = {
//方法体
}
注意:
- 在参数类型后面加一个
*
号,表示参数可以是0个或多个 - 一个方法有且只能有一个变长参数,并且变长参数要放到参数列表的最后边
示例:
定义一个计算若干值相加的方法;调用方法,传入以下数据:1,2,3,4,5
def getSum(numbers:Int*) = numbers.sum
val sum1 = getSum() //sum1: Int = 0
val sum2 = getSum(1, 2, 3, 4, 5) //sum2: Int = 15
1.5 方法调用方式
在编写spark、flink程序时,会经常用到这些方法调用方式。
1.5.1 后缀调用法
这种跟Java没区别。
语法:
对象名.方法名(参数)
示例:
调用Math.abs ,用来求绝对值。
Math.abs(-1) //Scala自动用res0去接收,即res0: Int = 10
println(res0) //10
1.5.2 中缀调用法
后缀调用法里如果小括号过多,容易混乱,所以引出中缀调用法
语法:
对象名 方法名 参数
注意 :如果有多个参数,使用括号括起来。
示例:
调用Math.abs ,用来求绝对值。
Math abs -1
扩展:操作符即方法
在Scala中,所有操作符都是方法,操作符时一个方法名字是符号的方法。
1.5.3 花括号调用法
语法:
Math.abs{
//表达式1
//表达式2
}
注意 :方法只有一个参数,才能使用花括号调用法。
示例:
调用Math.abs ,用来求绝对值。
Math.abs{
println("求绝对值!")
-40
}
//结果为:
//求绝对值!
//res0: Int = 40
1.5.4 无括号调用法
如果方法没有参数,可以省略方法名后面的括号。
示例:
定义一个无参数的方法,打印“Hi”;使用无括号调用法调用该方法。
def sayHi() = println("Hi") //sayHi: ()Unit
sayHi() //结果为:Hi
sayHi //结果为:Hi
注意:
- 在Scala中,如果方法的返回值类型是Unit类型,这样的方法称之为过程(procedure)
- 过程的等号(=)可以省略不写
def sayHi2() { println("Hi") } //sayHi: ()Unit
sayHi2() //结果为:Hi
sayHi2 //结果为:Hi
2.函数
scala支持函数式编程,将来编写Spark/Flink程序会大量使用函数。(Java里方法和函数是一回事,但Scala里面不是的)
2.1 定义函数
语法:
val 函数变量名 = (参数名:参数类型,参数名:参数类型...) => 函数体
注意:
- Scala中函数是一个对象(变量)
- 类似于方法,函数也有参数列表和返回值
- 函数定义不需要使用
def
定义 - 无需指定返回值类型
示例:
定义一个计算两个整数之和的函数,并调用该函数。
val getSum = (a:Int, b:Int) => a + b //getSum: (Int, Int) => Int = <function2>
val sum = getSum(11, 22) //sum: Int = 33
2.2 方法和函数的区别
Scala中函数和方法的具体区别如下:
- 方法是隶属于类或者对象的,在运行时,它是加载到JVM的方法区中;函数对象赋值给一个变量,在运行时,它是加载到JVM的堆内存中
- 函数是一个对象,继承自FunctionN,函数对象有apply, curried, toString, tupled这些方法;方法则没有
- 结论:Scala中函数是对象,而方法是属于对象的,所以理解为:方法归属于函数
示例:
演示方法无法赋值给变量
//1.定义方法
def add(x:Int, y:Int) = x + y
//2.尝试将方法赋值给变量
//val a = add(1, 2) //错误,这样是在“调用方法”,而不是把方法赋值给变量
val a = add
//3.上述代码会报错
<console>:12: error: missing argument list for method add
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `add _` or `add(_,_)` instead of `add`.
val a = add
//原因是:方法不是一个对象,不能直接赋值给变量a
2.3 方法转换为函数
有时需要将方法转换为函数,比如:作为变量传递。
格式:
val 变量名 = 方法名 _ //格式为:方法名+空格+下划线
示例:
定义一个方法用来计算两个整数之和;将该方法转换为一个函数,并赋值给变量。
//1.定义一个方法用来计算两个整数之和
def add(x:Int, y:Int) = x + y
//2.将该方法转换为一个函数,并赋值给变量
val a = add _ //a: (Int, Int) => Int = <function2>
//3.调用函数,用来获取两个整数之和
val result = a(1, 2)
3. 案例:打印nn乘法表
方式一:通过方法实现
//1.定义一个方法,接受一个整型参数
def printMT(n:Int) = { //Multiplication Table
//2.通过for循环嵌套实现,根据传入的整数,打印对应的乘法表
for(i <- 1 to n; j <- 1 to i) {
print(s"${j} * ${i} = ${j * i}\t")
if(j==i) println()
}
}
//3.调用方法
printMT(5)
//优化版
def printMT(n:Int) = for(i <- 1 to n; j <- 1 to i) print(s"${j} * ${i} = ${j * i}\t" + (if(j==i) "\r\n" else "\t"))
方法二:通过函数实现
val printMT = (n:Int) => {
for(i <- 1 to n; j <- 1 to i) {
print(s"${j} * ${i} = ${j * i}\t")
if(j==i) println()
}
}
printMT(5)