函数式编程之基础
1. 面向函数式编程概述
1.1 面向对象编程
解决问题,分解对象,行为,属性,然后通过对象的关系以及行为的调用来解决问题。
Scala语言是一个完全面向对象编程语言。所谓万物皆对象。
对象的本质:对数据和行为的一个封装。
比如对象:用户;行为:登录、连接JDBC、读取数据库;属性:用户名、密码。
1.2 面向函数式编程
解决问题时,将问题分解成一个一个的步骤,将每个步骤进行封装(函数),通过调用这些封装好的步骤,解决问题。
Scala 语言是一个完全函数式编程语言。万物皆函数。
函数的本质:函数可以当做一个值进行传递。对功能的封装。
在Scala中函数式编程和面向对象编程完美融合在一起了。
比如:请求->用户名、密码->连接 JDBC->读取数据库
1.3 Scala函数和Java方法的区别
- 称呼不同:Java中功能的封装是方法,Scala中称为函数。
- 只有在Java类中才能声明方法; 函数可以在任何地方声明,不受到限制。
- 函数没有重载和重写的概念;Java方法可以进行重载和重写。
- Scala中函数可以嵌套定义函数,而Java方法不能。
scala
def main(args: Array[String]): Unit = {
// (1)Scala 语言可以在任何的语法结构中声明任何语法
import java.util.Date
new Date()
// (2)函数没有重载和重写的概念,程序报错
def test(): Unit ={
println("无参,无返回值")
}
test()
def test(name:String):Unit={
println()
}
//(3)Scala 中函数可以嵌套定义
def test2(): Unit ={
def test3(name:String):Unit={
println("函数可以嵌套定义")
}
}
}
3. 函数基础
3.1 函数基本语法
基本语法:
3.2 Scala函数和Scala方法的区别
Scala中存在函数和方法两个概念,区别比较小:
- Scala函数和Scala方法的声明位置不同,Scala方法声明在类中,Scala函数声明在Scala方法中。
- Scala方法可以通过
对象.方法
调用,Scala函数直接调用。 - Scala函数是一个对象,可以赋值给其他变量,而Scala方法是组成类中一部分。
- Scala中函数可以嵌套定义。Scala方法就是在类中的成员,不能定义在其他位置。
- Scala方法可以重载,和Java方法类似。
scala
object FunctionDemo {
def main(args: Array[String]): Unit = {
// 函数可以嵌套定义
def test(): Unit = {
println("test function.....")
}
// 如果方法和函数名称相同,优先调用函数,调用方法需要对象.test()指明
test()
this.test()
// 如果函数参数没有可以简写函数名
test
}
def test():Unit={
println("test method.....")
}
def test(age:Int): Unit = {
println("test method.....")
}
}
运行结果:
Java字节码规范没有函数概念,函数怎么实现的呢?
函数的本质:将Scala的字节码反编译可以看出,如下图,函数就是Java字节码中的方法。增加了修饰符private static final
, 其中final
导致函数不能被重写重载,static
使得函数可以直接调用,而不需要通过对象调用,可以发现字节码中函数名称发生变化,这是因为避免和Scala方法发生冲突。另外main方法中调用test$1();
也说明就近原则优先调用的是函数。
3. 函数定义
有以下这些情况:
- 函数 1:无参,无返回值
- 函数 2:无参,有返回值
- 函数 3:有参,无返回值
- 函数 4:有参,有返回值
- 函数 5:多参,无返回值
- 函数 6:多参,有返回值
scala
def main(args: Array[String]): Unit = {
// 函数1:无参,无返回值
def test1(): Unit = {}
// 函数2:无参,有返回值
def test2(): String = {
"scala"
}
// 没有参数的函数被调用时可以简写
test1
test2
// 函数3:有参,无返回值
def test3(args:String): Unit={
println("test3")
}
// 需要注意Scala函数如果有参数不能简写成以下格式:
// test3 "demo" // 只有Scala方法支持这么写
// 函数4:有参,有返回值
def test4(args:String): String={
println(args)
"test4"
}
// 函数5:多参,无返回值
def test5(name:String, age:Int): Unit={
println("name: "+ name+ "age: "+age)
}
// 函数6:多参,有返回值
def test6(name:String, age:Int):(String, Int)={
(name, age)
}
}
4. 函数参数
- 可变参数
- 如果参数列表中存在多个参数,那么可变参数一般放置在最后
- 参数默认值,一般将有默认值的参数放置在参数列表的后面
- 带名参数
scala
def main(args: Array[String]): Unit = {
// 可变参数
def test(s: String*): Unit = {
println(s)
}
// 有输入参数:输出 Array
test("Hello", "Scala")
// 无输入参数:输出 List()
test()
// 多个参数可以类型加上*表示,如果参数列表中存在多个参数,那么可变参数一般放置在最后
def test2(name: String, s: String*): Unit = {
println(name + "," + s)
}
test2("jinlian", "dalang")
// (3)参数默认值, 因为参数默认是val声明, 参数默认值需要在声明的时候赋值
def test3(name: String, age: Int = 30): Unit = {
println(s"$name, $age")
}
// 如果参数传递了值,那么会覆盖默认值
test3("jinlian", 20)
// 如果参数有默认值,在调用的时候,可以省略这个参数
test3("dalang")
// 一般情况下,将有默认值的参数放置在参数列表的后面
def test4(sex: String = "男", name: String): Unit = {
println(s"$name, $sex")
}
// 如果默认值参数放置在前面,由于参数传值顺序默认从左到右,如果传参按照自己的顺序,需要带名参数
//(4)带名参数
test4(name = "ximenqing")
}
5. 函数至简原则
所谓的至简原则,其实就是Scala的作者为了开发人员能够大幅度提高开发效率。通过编译器的动态判定功能,帮助我们将函数声明中能简化的地方全部都进行了简化。也就是说将函数声明中那些能省的地方全部都省掉。函数至简原则:能省则省💋
- return可以省略,Scala会使用函数体的最后一行代码作为返回值。
- 如果函数体只有一行代码,可以省略花括号。
- 返回值类型如果能够推断出来,那么可以省略和返回值类型一起省略。
- 如果有return,则不能省略返回值类型,必须指定。
- 如果函数明确声明Unit,那么即使函数体中使用return关键字也不起作用。
- Scala如果期望是无返回值类型,可以省略等号。
- 如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加。
- 如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略。
- 如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略。
scala
def main(args: Array[String]): Unit = {
def test(): Unit = {
println("test....")
}
// 如果函数有返回值,可以省略return
def test1(): String = {
"hehe"
}
// 如果函数的逻辑代码只有一行,可以将大括号省略
def test2(): String = "hehe"
// 如果能够通过返回值推断出返回值类型,返回值类型可以省略
def test3() = "hehe"
// 如果函数参数没有,可以省略括号
// 因为省略了很多语法内容,所以函数声明和变量声明很像了,所以必须使用def关键字区分
def test4 = "hehe"
println(test4) // println(test4()) 调用不加括号的函数不能在调用的时候加上括号
// 如果函数有返回值,但是函数声明为Unit,此时return不起作用
def test5(): Unit = {
return "hehe"
}
println(test5())
// def和函数名都省略的时候,称之为匿名函数, 需要使用=>进行关联
() => {
"test"
}
}
运行结果: