一、函数式编程

1.1 函数式编程的概念

  • 函数式编程(Functional Programming)是一种编程范型,它将计算机程序视为函数的组合,强调使用纯函数(Pure Function)、高阶函数(Higher-order Function)、lambda
    表达式(Lambda Expression)等来处理数据。函数式编程中的函数被视为数学上的函数,它不会修改自己的输入,也不会对程序外部环境产生影响,而只是根据输入返回一个新的值。
  • 与传统的命令式编程(Imperative
    Programming)相比,函数式编程更加注重计算结果而非计算过程,使得代码更加简洁、可读性更强,适合处理大规模数据和并发处理等场景。同时,函数式编程也具有很好的可测试性、可维护性和可扩展性,能够更好地应对软件开发中的变化和需求。
  • 在函数式编程中,通常采用不可变数据(Immutable Data)的方式来避免状态变化带来的问题,并使用函数组合的方式来实现复杂的逻辑,以及利用 Lazy Evaluation
    的惰性求值策略来提高程序的性能。这些特性都让函数式编程在处理数据和逻辑上具有独特的优势。

1.2 函数和方法的区别

在Java中,我们有个很重要的概念:方法。它和函数的本质都是实现某种特定功能的代码块,因此可能乍一看感觉函数和方法是同一个意思,其实不然。

  • 函数(Function)是指独立于任何类或对象的代码块,它接收一些输入参数,对这些参数进行处理并返回值,不依赖外部状态,也不修改外部状态。函数可以被其他函数或方法调用,以实现更加复杂的功能。
  • 方法(Method)是指定义在一个类或对象中的函数,它与类或对象的状态和属性紧密相关联,可以访问和修改这些状态和属性。方法除了可以像函数一样被调用,还可以通过类或对象来调用,实现对内部状态和属性的操作。

在面向对象编程中,方法经常被用来执行某个特定对象的行为,也经常涉及到继承、多态等概念。而在函数式编程中,函数更加强调纯函数和不可变数据的使用,通常不涉及到类和对象的概念。
总的来说,函数和方法都是实现某种特定功能的代码块,但它们的使用方式和概念在不同的编程语言和编程范式中可能存在差异。

1.3 面向对象编程和函数式编程的区别

面向对象编程(Object-Oriented Programming,OOP)和函数式编程(Functional Programming,FP)是两种不同的编程范式。

1.3.1 面向对象编程

面向对象编程相信有过Java基础的同学肯定不会陌生,面试也会经常出现一个送命题:请谈谈你对面向对象编程概念的理解。 那么什么是对象?什么是面向对象编程呢?

  • Java中,”万物皆对象”。这样类似的说法有很多,比如 SQL 中:”万物皆表”,Git 中:”万物皆版本”等等…这些都反映了一种观念,即我们可以将所有的事物都抽象为同一种基本元素,从而简化和统一对待各种不同的物品或概念。
  • 那么什么是对象呢?它当然不是指现实生活中的男女朋友,在 Java 中,对象就是实体化的类,保存了类中定义的属性和方法。在 Java 中,几乎所有的数据类型和操作都可以看作是对象和方法的组合。
  • “万物皆对象”这个理念认为,任何事物都可以抽象为一个对象,并通过对象之间的交互来实现各种功能。这种思想强调了代码的可重用性、可维护性和可扩展性。
  • 面向对象编程的含义是将程序中的数据和操作封装在一起,形成一个个对象,并通过对象之间的协作和交互来实现程序的功能。面向对象编程强调了程序的模块化和组件化,使得程序更易于理解、修改和维护。

1.3.2 函数式编程

  • 函数式编程(Functional Programming)是一种以函数为核心的编程范式,与传统的命令式编程不同,它强调将计算过程尽量转换为若干个函数之间的组合和运算,而不是通过对程序状态进行赋值和修改来实现。
  • 在函数式编程中,函数被视为一等公民,可以像其他数据类型一样被传递、返回和组合。同时,函数式编程还强调避免副作用,即尽可能地消除函数对外部环境的依赖,使得同样的输入能够得到同样的输出。
  • “函数即数据”这个理念认为,函数和数据在函数式编程中是等价的,都可以被看作是一种数学上的值。函数式编程将复杂的问题分解为多个简单的函数,并通过函数的组合和变换来解决问题,使得程序更加清晰、简洁和易于理解。
  • 函数式编程强调无状态性和不可变性,即不改变原有的数据结构和状态。这种特性使得函数式编程更具有可重用性和可测试性,在并发编程中也有着特别的优势。

1.3.3 它们的不同之处

  1. 编程思想不同
    面向对象编程强调将程序中的数据和操作封装在一起,形成一个个对象,并通过对象之间的协作和交互来实现程序的功能。而函数式编程则强调将计算过程尽量转换为若干个函数之间的组合和运算,而不是通过对程序状态进行赋值和修改来实现。

  2. 松耦合度不同
    面向对象编程中,不同对象之间的协作和交互需要通过公共接口来实现,因此面向对象编程具有较强的耦合性,即类之间的关系比较紧密。而函数式编程中,函数之间的协作和交互通过参数和返回值来实现,因此函数式编程具有较弱的耦合性,即函数之间的关系比较松散。

  3. 副作用处理不同
    副作用指对外部环境产生的影响,包括对变量值、I/O、数据库等的修改。面向对象编程通常通过封装保护数据,控制副作用的发生;而函数式编程则强调避免副作用,即尽可能地消除函数对外部环境的依赖,使得同样的输入能够得到同样的输出。

  4. 可变性处理不同
    面向对象编程中,对象的状态可以被改变;而函数式编程强调不可变性,即一旦一个值被定义后,其值就不能被更改。

  5. 代码风格不同
    面向对象编程中,常常会使用类、对象、继承等关键字和概念,代码的风格比较严谨、精确。而函数式编程则注重表达式的简洁和流畅,代码的风格比较自然和直观。

1.3.4 两种编程范式的运用

  • 面向对象编程是最早流行起来的编程范式之一,现在几乎所有主流的编程语言都支持面向对象编程。例如,Java、C++、Python、Ruby、Scala、Swift、Kotlin、JavaScript等。
  • 函数式编程在近年来逐渐流行起来,现在也有越来越多的编程语言开始支持函数式编程范式。例如,Haskell、Scala、Erlang、Swift、JavaScript、Python、Kotlin等。
  • 可以看出,很多编程语言都同时使用了这两种编程范式理念,总之,OOP 和 FP 都是有效的编程方法,各有优劣,可根据具体情况选择适合的编程范式。

二、初步接触函数式编程

Kotlin中函数的众多写法

  • 在 Java 中,声明一个方法的写法比较单一,比如,这是一个计算两个值相加的方法:
1
2
3
4
5
class Fun_ {
public int addNumbers(int a, int b) {
return a + b;
}
}

当然,通过改变访问修饰符(public)、返回类型(int)、形参列表(int a, int b)等,可以实现方法的一些变化,但是总体来说结构是比较固定的。

  • 接下来我们看一下 Kotlin 中函数的各种花式写法:
  1. 普通函数
1
2
3
fun add(a: Int, b: Int): Int {
return a + b
}
  1. 省略返回类型的函数:
1
fun multiply(a: Int, b: Int) = a * b
  1. 单表达式函数:
1
fun isEven(num: Int) = num % 2 == 0
  1. 带默认参数的函数:
1
2
3
4
5
fun showMessage(message: String, times: Int = 1) {
repeat(times) {
println(message)
}
}
  1. 带具名参数调用的函数:
1
showMessage(message = "Hello", times = 3)
  1. 使用 lambda 表达式的函数:
1
2
val numbers = listOf(1, 2, 3, 4, 5)
numbers.forEach { println(it) }
  1. 带有高阶函数参数的函数:
1
2
3
4
5
6
7
8
fun processNumbers(numbers: List<Int>, action: (Int) -> Unit) {
for (number in numbers) {
action(number)
}
}

val numbers = listOf(1, 2, 3, 4, 5)
processNumbers(numbers) { println(it) }
  1. 使用函数类型别名的函数:
1
2
3
4
5
6
7
8
9
typealias Processor = (Int) -> Unit

fun processNumbers(numbers: List<Int>, action: Processor) {
for (number in numbers) {
action(number)
}
}
val numbers = listOf(1, 2, 3, 4, 5)
processNumbers(numbers) { println(it) }
  • 没看错吧,这么多?!还真有这么多,而且还不止这么多,如果加上访问修饰符等等的变化,那可真是百花齐放了。
  • Kotlin 中的函数可以具有默认参数、具名参数调用、扩展函数、局部函数等特性,这些特性使得 Kotlin 的函数非常灵活和易于使用。
  • 见识到函数式编程的威力了吗?首先在函数的申明和调用方面就如此的灵活多变,它可以让程序员更加关注解决问题应该实现什么逻辑,而不是如何实现。
  • 它通过将函数看作一等公民以及使用大量的高阶函数和 lambda 表达式等特性,使得代码更加简洁、易于理解和维护,同时也提高了代码的可复用性和可测试性。

三、总结

  • 在见识了 Kotlin 的函数世界后,我第一感觉是好像代码是变得简洁了,但是貌似可读性并没有增加,反而是更让人摸不着头脑。可能还需要熟练之后才能体会到其带来的优势。
  • 还有一个问题,我觉得如此众多的写法,而不同的程序员又有不同的编码习惯,这可能会导致不同的 Kotlin 程序员之间的代码互读性进一步变差。
  • 你是什么想法呢?可以在下方评论区进行讨论或者直接联系我!