opoojkk

函数与方法:一个容易被忽视的编程概念差异

lxx
目次

从学习C语言接触计算机开始,我总以为函数和方法不过是两个不同的名称而已,只不过是习惯不同。直到阅读Rust语言的文档时,同时出现了"方法"和"函数"两个术语,我才意识到这其中的差异。

事实上,它们不能说是两个完全不同的概念,但确实存在区别。

什么是方法(Method) #

在面向对象编程中,方法(英语:Method;德语:Methode;法语:Méthode)指的是属于类别(所谓的类方法、静态方法或工厂方法)或者对象(所谓的实例方法)的子程序。如同过程化程序设计的程序,一个方法通常由一系列语句组成,并以此完成一个动作。它可以通过输入一组参数来指定所需的动作,且部分方法可能会有输出值(所谓的返回值)。方法的目的是提供一个机制,以访问(读取和写入)对象或类别的私有数据

参考:Wikipedia - Method (computer programming)

函数与方法的区别 #

Wikipedia关于函数的条目中有这样一段描述:

Some programming languages, such as COBOL and BASIC, make a distinction between functions that return a value (typically called “functions”) and those that do not (typically called “subprogram”, “subroutine”, or “procedure”); some, such as C, C++, and Rust, only use the term “function” irrespective of whether they return a value or not; others, such as ALGOL 60 and PL/I, only use the word procedure. Some object-oriented languages, such as Java and C#, refer to functions inside classes as “methods”.

参考:Wikipedia - Function (computer programming)

如果你去搜索相关讨论,会发现基本能形成的共识是:定义在类内部的称为Method(方法),定义在类外部的称为Function(函数)

各语言中的实践 #

理解了这个理论后,我们再来看各种语言的实现就清晰多了。

Java #

Java中所有的"函数"都必须定义在类中,无论是成员方法还是静态方法,因此统一称为方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
public class Calculator {
    private int offset = 0;
    
    // 实例方法
    public int add(int a, int b) {
        return a + b + offset;
    }
    
    // 静态方法
    public static int multiply(int a, int b) {
        return a * b;
    }
}

// 使用
Calculator calc = new Calculator();
calc.add(1, 2);              // 调用实例方法
Calculator.multiply(3, 4);    // 调用静态方法

Kotlin #

Kotlin既可以像Java一样在类内部定义,这些被称为方法;也可以定义在类的外部,因此扩展函数被称为"扩展函数",而不是"扩展方法"。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// 顶层函数
fun add(a: Int, b: Int): Int {
    return a + b
}

class Calculator(private var offset: Int = 0) {
    // 成员方法
    fun add(a: Int, b: Int): Int {
        return a + b + offset
    }
}

// 扩展函数(注意是"函数"不是"方法")
fun Calculator.subtract(a: Int, b: Int): Int {
    return a - b
}

C++ #

C++同时支持函数和方法,但在术语上都统称为"function"。

函数(Function):定义在类外部的独立函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 全局函数
int add(int a, int b) {
    return a + b;
}

// 命名空间中的函数
namespace Math {
    int multiply(int a, int b) {
        return a * b;
    }
}

方法(Method):定义在类内部的成员函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class Calculator {
public:
    // 实例方法(成员函数)
    int add(int a, int b) {
        return a + b + offset;
    }
    
    // 静态方法
    static int multiply(int a, int b) {
        return a * b;
    }
    
private:
    int offset = 0;
};

// 使用
Calculator calc;
calc.add(1, 2);              // 调用实例方法
Calculator::multiply(3, 4);   // 调用静态方法

Go #

Go语言明确区分函数和方法的概念。

函数(Function):独立定义的函数

1
2
3
4
5
6
7
8
// 包级别函数
func Add(a, b int) int {
    return a + b
}

func main() {
    result := Add(1, 2)
}

方法(Method):绑定到特定类型的函数(通过接收者receiver定义)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
type Calculator struct {
    offset int
}

// 值接收者方法
func (c Calculator) Add(a, b int) int {
    return a + b + c.offset
}

// 指针接收者方法
func (c *Calculator) SetOffset(offset int) {
    c.offset = offset
}

// 使用
calc := Calculator{offset: 10}
calc.Add(1, 2)           // 调用方法
calc.SetOffset(20)       // 调用方法

Rust #

Rust也明确区分函数和方法,这也是最初让我意识到这个差异的语言。

函数(Function):独立定义的自由函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 自由函数(free function)
fn add(a: i32, b: i32) -> i32 {
    a + b
}

// 模块中的函数
mod math {
    pub fn multiply(a: i32, b: i32) -> i32 {
        a * b
    }
}

方法(Method):在impl块中为类型定义的函数

 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
struct Calculator {
    offset: i32,
}

impl Calculator {
    // 关联函数(Associated Function) - 类似静态方法,没有self参数
    fn new(offset: i32) -> Self {
        Calculator { offset }
    }
    
    // 实例方法 - 带有self参数
    fn add(&self, a: i32, b: i32) -> i32 {
        a + b + self.offset
    }
    
    // 可变引用方法
    fn set_offset(&mut self, offset: i32) {
        self.offset = offset;
    }
}

// 使用
let mut calc = Calculator::new(10);  // 调用关联函数
calc.add(1, 2);                      // 调用实例方法
calc.set_offset(20);                 // 调用可变方法

总结 #

语言函数(Function)方法(Method)特点
Java不存在所有都是方法必须定义在类中
Kotlin顶层函数、扩展函数类成员方法同时支持两者
C++全局函数、命名空间函数类成员函数术语上都称为function
Go包级别函数带接收者的函数明确区分,方法必须有receiver
Rust自由函数、模块函数impl块中的函数明确区分,有self的是方法,无self的是关联函数

核心区别其实很简单:方法(Method)与类型/对象绑定,函数(Function)独立存在。

标签:
Categories: