【问题标题】:What is ...interface{} as an argument什么是 ...interface{} 作为参数
【发布时间】:2013-09-05 06:33:44
【问题描述】:

我指的是http://golang.org/pkg/log/上func Printf的来源

func Printf(format string, v ...interface{})
Printf calls Output to print to the standard logger. Arguments are handled in the manner of fmt.Printf.

我有两个问题:

  1. 什么是“...”?
  2. ...interface{} 是什么意思?

非常感谢

【问题讨论】:

    标签: go


    【解决方案1】:

    这并不完全是特定的,因此与其他回答者不同,我将采用更一般的路线。

    关于变量参数 (...)

    ... 这里称为“省略号”,表示函数可以接收可变数量的参数,通常称为 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);
    

    【讨论】:

      【解决方案2】:

      Go 文档非常好,语言规范写得非常好,易于理解。为什么不看看?

      http://golang.org/ref/spec#Function_types

      http://golang.org/ref/spec#Passing_arguments_to_..._parameters

      http://golang.org/ref/spec#Interface_types

      在您的浏览器中按 Ctrl-F 并寻找 ...interface{} 会启发您。

      【讨论】:

        【解决方案3】:

        文档直接回答了您的问题。这是链接和相关部分:

        http://golang.org/doc/effective_go.html

        Printf 的签名使用 ...interface{} 类型作为其最终参数,以指定在格式之后可以出现任意数量的参数(任意类型)。

        func Printf(format string, v ...interface{}) (n int, err error) {
        

        在函数 Printf 中,v 的行为类似于 []interface{} 类型的变量,但如果将其传递给另一个可变参数函数,则它的行为类似于常规参数列表。下面是我们上面使用的函数 log.Println 的实现。它将其参数直接传递给 fmt.Sprintln 以进行实际格式化。

        // Println prints to the standard logger in the manner of fmt.Println.
        func Println(v ...interface{}) {
            std.Output(2, fmt.Sprintln(v...))  // Output takes parameters (int, string)
        }
        

        在对 Sprintln 的嵌套调用中,我们在 v 之后写 ... 以告诉编译器将 v 视为参数列表;否则它只会将 v 作为单个切片参数传递。

        【讨论】:

        • 我明白了。这很有帮助。 interface{} 作为一种类型是否意味着我们可以将任何类型传入其中?就像我们可以传入 int 或 string 一样?
        • Go 中的接口只是一组方法。如果 type 有所有接口方法,那么它会自动实现这个接口。 interface{} 是一个带有空方法集的接口。这意味着任何类型都实现接口{}。
        猜你喜欢
        • 2012-08-14
        • 2013-09-19
        • 2011-06-23
        • 1970-01-01
        • 2017-07-13
        • 2013-09-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多