这并不完全是特定的,因此与其他回答者不同,我将采用更一般的路线。
关于变量参数 (...)
... 这里称为“省略号”,表示函数可以接收可变数量的参数,通常称为 varargs(或 var-args,或其他拼写)。这称为variadic function。
简单地说,根据下面的签名:
func Printf(format string, v ...interface{}) (n int, err error) {}
Printf 将需要string 类型的第一个参数,然后是interface{} 类型的0 到N 个参数。下一节将详细介绍该类型。
虽然提供任意数量的参数的能力看起来非常方便,并且在此处不涉及太多细节以免偏离主题,但它带有一些警告,具体取决于语言中的实现:
我会留给你从上面的资源中查找原因。
在空界面 (interface{})
这个语法位更特定于 Go,但提示在名称中:interface。
接口(或更接近 Go 的范式,协议)是一种类型,它定义了其他对象要遵守的契约。根据interfaces in computing 上的这篇Wikipedia 文章(粗体字强调,斜体字更正):
在面向对象的语言中,**术语“接口”通常用于定义不包含数据但公开定义为方法的行为的抽象类型。具有与该接口对应的所有方法的类被称为实现该接口。此外,一个类可以[在某些语言中])实现多个接口,因此可以同时具有不同的类型。
接口因此是类型定义;在任何可以交换对象的地方(在函数或方法调用中)可以根据接口而不是特定类来定义要交换的对象的类型。这允许以后的代码使用相同的函数来交换不同的对象类型; _[旨在]_ 通用且可重复使用。
现在回到 Go 的空界面
Go 是一种强类型语言,具有多种内置类型,包括 Interface Types,它们在当前 (1.1) 语言规范中将其描述为 gollows:
接口类型指定了一个称为其接口的方法集。接口类型的变量可以存储任何类型的值,方法集是接口的任何超集。这种类型被称为实现了接口。
接下来,您会看到Printf 的签名interface{}(粗体字)中看到的结构:
一个类型实现了包含其方法的任何子集的任何接口,因此可以实现几个不同的接口。例如,所有类型都实现空接口:
interface{}
这基本上意味着任何类型都可以表示为“空接口”,因此Printf 可以接受这些可变参数的任何类型的变量。
与其他语言的快速比较
从历史上看,printf 这个名称来自 C 函数和同名二进制文件,printf 意思是“打印格式”,尽管在早期的语言中有可变参数打印函数,并且可变参数函数用于许多其他场景。然而,printf 通常被认为是这种使用的主要例子。它在 C 中的签名是:
int printf(const char *format, ...);
由于它们的实用性,可变参数和printf 熟悉的面孔出现在大多数语言中...
在 Java 中,printf 以多种形式存在,特别是来自 PrintStream 类:
public PrintStream printf(String format, Object... args)
其他一些语言不打扰指定变量参数并使其隐含,例如在 JavaScript 中,函数中的 arguments 特殊变量允许访问传递给函数的任何参数,无论它们是否与原型匹配。
console.log() 方法是类似于printf 的示例,为清楚起见扩展了以下伪签名(但实际上只是使用arguments):
console.log(obj1 [, obj2, ..., objN);
console.log(msg [, subst1, ..., substN);