关于go的interface机制

一篇速记

1. 基本实现

照例先贴两个链接吧(好的,我就是粘贴怪了)
Go Data Structures: Interfaces
golang的interface剖析
可以先看第一篇,再看第二篇。
总结一下两篇文章的大意吧

  • interface结构占16个字节(64位机器),前8位是一个tab指针,指向全局的itable(hash表)的一个tab表项(具体的结构体定义可以参考第二篇链接)。后8位是一般是一个数据指针,指向实际存放的数据。
  • 映射关系:(interface接口,实际的数据类型) -> tab表项,tab表项是在运行期间动态计算的(当然不会在编译时来算,因为大部分的tab表项对应的映射关系在程序中是用不到的),具体来说,下面这行代码
var2 := (interface{})(var1)

会陷入runtime(我猜测一种实现的方法是,在编译的时候把这条语句编译为一个runtime的函数调用之类的。)
runtime则负责查找hash表中是否有(interface{}, typeof(var1))对应的tab表项,如果找到了,var2的tab指针就指向该表项,否则就新建一个tab表项,加入到hash表中。

  • 第一点中说到一般是指向数据的指针是因为如果数据本身大小不超过8字节,就没有必要额外去申请堆内存,然后存一个指向堆数据的指针,而直接把数据本身copy到8字节里就可以了

2. 进一步讨论

在展开接下来的讨论前,我再贴一个链接。
Golang method with pointer receiver [duplicate]
具体来说,当我们定义type im struct后,定义方法有两种情况

type iface interface{
	add()
}
func (i im) add() {
//***** method 1
}
func (i *im) add(){
//***** method 2
}

var a iface
a = im{}    // 接口赋值情况1
a = &im{}   // 接口赋值情况2

如果我们采用method 1的定义方法,会发现两种接口赋值均能够通过编译。但是如果采用method 2的定义方法,发现只有接口赋值情况2能够通过编译。
根本原因在于指针类型会继承原类型的方法,但是原类型不能够继承指针类型的方法。一种猜测的实现方法,比如上面的method1可以在编译时生成一个对偶的指针方法。

func (i *im) add{
	(*i).add()
}

这样理解也符合go的语法

func (i im) add{
// **** method 1
}
var h := &im{}
h.add()

虽然我们并没有显式地用*h,通常是认为译器自动地加上了*号,不过这也可以认为是指针类型继承了原类型的add方法,h.add() -> (*h).add()只不过是编译器的内联优化罢了。
另外这样理解也与第一节中的第一个链接中强调的(*Binary).string的调用和(Binary).string的调用区别相吻合。
这样解决了采用method1的定义方法,接口赋值2能够通过编译的问题,那为什么不类似地整一个为原类型准备的对偶函数呢?例如下面这样

func (i *im) add(){
//***** method 2
}
func (i im) add(){   // method2的对偶函数
	(&i).add()
}

好吧,首先这个与go的语法就没有之前那样吻合了,因为当我们写i.add()时希望的是达到传引用的效果,如果是调用上面的对偶函数,会先进行一波copy,传引用的语义就消失了。另外呢,这样的对偶函数失去了传引用语义后,原有add函数的行为的表现与预期可能并不相符(比如添加了这个后也没办法解决stackoverflow上那老哥的问题)。

不过上面的解释只是帮助理解interface的语义,具体编译器也不一定是怎么做的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在 Go ,将 `interface{}` 转换为 `int64` 有几种不同的方法,具体方法取决于实际情况和使用场景。以下是一些可能的方法: 方法一:使用类型断言 如果你知道 `interface{}` 变量存储的确切类型是 `int64`,那么可以使用类型断言将其转换为 `int64`。以下是一个示例代码: ``` var val interface{} = int64(42) i, ok := val.(int64) if ok { // val 是 int64 类型 fmt.Printf("%T, %v\n", i, i) // 输出:int64, 42 } else { // val 不是 int64 类型 fmt.Println("val 不是 int64 类型") } ``` 如果 `val` 变量存储的不是 `int64` 类型,那么类型断言将失败,`i` 变量的值将是 `0`,`ok` 变量的值将是 `false`。 方法二:使用类型判断 如果你不确定 `interface{}` 变量存储的确切类型是什么,可以使用类型判断来判断它是否可以转换为 `int64`。以下是一个示例代码: ``` var val interface{} = int64(42) switch val.(type) { case int64: i := val.(int64) fmt.Printf("%T, %v\n", i, i) // 输出:int64, 42 case int: i := int64(val.(int)) fmt.Printf("%T, %v\n", i, i) // 输出:int64, 42 default: fmt.Println("val 不是整数类型") } ``` 在这个例子,`switch` 语句根据 `val` 的类型执行不同的操作。如果 `val` 是 `int64` 类型,则将其转换为 `int64`。如果 `val` 是 `int` 类型,则将其转换为 `int64`。如果 `val` 不是整数类型,则输出一条错误信息。 方法三:使用反射 Go 语言的反射机制允许你在运行时检查变量的类型,并以通用的方式操作变量。以下是一个示例代码,使用反射将 `interface{}` 变量转换为 `int64`: ``` import "reflect" var val interface{} = int64(42) v := reflect.ValueOf(val) if v.Kind() == reflect.Int64 { i := v.Int() fmt.Printf("%T, %v\n", i, i) // 输出:int64, 42 } else { fmt.Println("val 不是 int64 类型") } ``` 在这个例子,使用 `reflect.ValueOf` 将 `val` 转换为 `reflect.Value` 类型。然后使用 `v.Kind()` 检查 `v` 的类型,如果它是 `reflect.Int64` 类型,则使用 `v.Int()` 将其转换为 `int64` 类型。如果 `v` 不是 `reflect.Int64` 类型,则输出一条错误信息。 ### 回答2: 在Go语言,将`interface{}`类型转换为`int64`类型有两种常用的方法。第一种是使用类型断言,第二种是使用类型转换。 1. 使用类型断言(Type Assertion): ```go func toInt64(i interface{}) (int64, error){ if val, ok := i.(int64); ok { return val, nil } return 0, fmt.Errorf("无法将interface{}转换为int64") } ``` 在这个函数,我们首先使用`.(int64)`来尝试将`interface{}`类型的参数转换为`int64`类型。如果转换成功,就返回转换后的值和`nil`;否则,返回默认值`0`和一个错误信息。 2. 使用类型转换(Type Conversion): 在确定`interface{}`类型可以转换为`int64`时,可以直接使用类型转换操作符将其转换为`int64`类型: ```go func toInt64(i interface{}) (int64, error){ val, ok := i.(int64) if !ok { return 0, fmt.Errorf("无法将interface{}转换为int64") } return val, nil } ``` 在这个函数,我们将`interface{}`类型的参数转换为`int64`类型,并通过判断是否转换成功来返回转换后的值或错误信息。 需要注意的是,这两种方法都需要在接收结果的地方进行错误处理,以避免程序崩溃或产生意外结果。 ### 回答3: 要将 interface{} 转为 int64,可以通过以下步骤实现: 首先,使用类型断言来检查 interface{} 是否可以转换为 int64 类型。可以使用语法 `value, ok := 接口变量.(目标类型)`。 接下来,在条件语句判断断言的结果。如果断言成功,即 ok 为 true,则可以将 value 赋值给 int64 类型的变量。如果断言失败,即 ok 为 false,则说明无法将 interface{} 转为 int64。 以下是一个示例代码: ``` func convertToInt64(value interface{}) (int64, error) { result, ok := value.(int64) if ok { return result, nil } return 0, errors.New("无法将interface{}转为int64") } func main() { var val interface{} = int64(123) convertedVal, err := convertToInt64(val) if err != nil { fmt.Println("转换失败:", err) } else { fmt.Println("转换成功,结果为:", convertedVal) } } ``` 在上述代码,convertToInt64 函数用于将 interface{} 转为 int64。如果转换成功,则返回转换后的 int64 值,否则返回一个错误。 在 main 函数,我们定义了一个 interface{} 类型的 val 变量,并赋值为 int64 类型的值 123。然后调用 convertToInt64 函数进行转换,并根据转换结果进行相应的处理。 请注意,这种类型转换有一定的风险,因为 interface{} 可能包含不同的类型。因此,在进行类型断言和转换时,需要保证 interface{} 变量的实际类型与目标类型是匹配的,否则会导致运行时错误。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值