Go 接口(Interfaces)与反射(reflection)

本文探讨Go语言的接口和反射特性。介绍了接口的嵌套、继承、类型断言、类型判断,展示了如何使用sort包进行自定义排序。此外,讨论了空接口、反射的应用,包括如何获取和修改反射值,以及在类型信息获取上的重要作用。最后总结了Go的面向对象思想,强调了封装、继承和多态的实现方式。
摘要由CSDN通过智能技术生成

多态

Go里的接口类似C++多态
相同接口的变量在不同的时刻表现出不同的行为

type Namer interface {
    Method1(param_list) return_type
    Method2(param_list) return_type
    ...
}
实现接口时:指针变量值变量
接收者是*T 类型的方法OKERROR 存储在接口里的值没有地址
接收者是T类型的方法OK 自动解指针OK

将一个值赋值给一个接口时,编译器会确保所有可能的接口方法都可以在此值上被调用,因此不正确的赋值在编译期就会失败。


package main

import "fmt"

type SayHellor interface {
	SayHello()
}

type Man struct {
	name string
}

type Woman struct {
	name string
}

// Man和Woman都必须实现SayHello方法
func (m *Man)SayHello() {
	fmt.Println(m.name, "say hello")
}

func (w *Woman)SayHello() {
	fmt.Println(w.name, "say hello")
}

func main() {
	var m Man = Man{"Tom"}
	var manSayHello SayHellor = &m	// 必须传地址
	Say(manSayHello)

	w := &Woman{"Amy"}
	var womanSayHello SayHellor = w
	Say(womanSayHello)
}

// SayHellor对象可以变成Man或者Woman
func Say(people SayHellor) {
	people.SayHello()
}

接口嵌套接口

->接口继承接口,还是接口

type ReadWrite interface {
    Read(b Buffer) bool
    Write(b Buffer) bool
}

type Lock interface {
    Lock()
    Unlock()
}

type File interface {
    ReadWrite
    Lock
    Close()
}

接口的继承

当一个类型包含(内嵌)另一个类型(实现了一个或多个接口)的指针时,这个类型就可以使用(另一个类型)所有的接口方法。

type tester interface {
	show()
}

type implementTester struct {
	str string
}

func (i *implementTester) show() {
	fmt.Println("show...", i.str)
}

type Test struct {
	str string
	*implementTester
}

func main() {
	t := Test{"1111", &implementTester{"aaa"}}	// show... aaa
	t.show()
}

类型断言:检测和转换接口变量的类型

ok: 测试接口变量是否为类型 T
v: 接口变量转换成类型为T的值

if v, ok := 接口变量.(T); ok {  // checked type assertion
    Process(v)
    return
}
// 接口变量不是类型T

注意T是结构体指针类型


package main

import "fmt"
type SayHellor interface {
	SayHello()
}

type Man struct {
	name string

}

type Woman struct {
	name string
}

func (m *Man)SayHello() {
	fmt.Println(m.name, "say hello")
}

func (w *Woman)SayHello() {
	fmt.Println(w.name, "say hello")
}

func main() {
	var m Man = Man{"Tom"}
	var hellor SayHellor = &m
	if t, ok := hellor.(*Man); ok {
		// 是*Man类型
		fmt.Println("Man:", t)							// Man: &{Tom}
		fmt.Printf("The type of hellor is: %T\n", t)	// The type of hellor is: *main.Man
	}

	w := new(Woman)
	w.name = "Amy"
	hellor = w
	if t, ok := hellor.(*Woman); ok {
		// 是*Woman类型
		fmt.Println("Woman:", t)						// Woman: &{Amy}
		fmt.Printf("The type of hellor is: %T\n", t)	// The type of hellor is: *main.Woman
	}

}

类型判断:type-switch

	var m Man = Man{"Tom"}
	var hellor SayHellor = &m
	switch hellor.(type) {
	case *Man:
		fmt.Println("type Man")
	case *Woman:
		fmt.Println("type Woman")
	case nil:
		fmt.Printf("nil value: nothing to check?\n")
	default:
		fmt.Printf("Unexpected type %T\n", t)
	}

类型分类函数

func classifier(items ...interface{}) {
	for i, x := range items {
		switch x.(type) {
		case bool:
			fmt.Printf("Param #%d is a bool\n", i)
		case float64:
			fmt.Printf("Param #%d is a float64\n", i)
		case int, int64:
			fmt.Printf("Param #%d is a int\n", i)
		case nil:
			fmt.Printf("Param #%d is a nil\n", i)
		case string:
			fmt.Printf("Param #%d is a string\n", i)
		default:
			fmt.Printf("Param #%d is unknown\n", i)
		}
	}
}
 
func main() {
	classifier(13, -14.3, "BELGIUM", complex(1, 2), nil, false)
}

在这里插入图片描述

测试类是否实现了某个接口

var _ SayHellor = new(Man)
var _ 接口类型 = new(类)

如果没实现,IDE会报错
在这里插入图片描述

Eg: 使用sort包,自定义排序

sort包里部分源码:
一个接口类型Interface和快排排序Sort方法都在库里实现了。

package sort

// A type, typically a collection, that satisfies sort.Interface can be
// sorted by the routines in this package. The methods require that the
// elements of the collection be enumerated by an integer index.
type Interface interface {
	// Len is the number of elements in the collection.
	Len() int
	// Less reports whether the element with
	// index i should sort before the element with index j.
	Less(i, j int) bool
	// Swap swaps the elements with indexes i and j.
	Swap(i, j int)
}

// Sort sorts data.
// It makes one call to data.Len to determine n, and O(n*log(n)) calls to
// data.Less and data.Swap. The sort is not guaranteed to be stable.
func Sort(data Interface) {
	n := data.Len()
	quickSort(data, 0, n, maxDepth(n))
}

那么使用时,只需要实现接口方法再调用Sort函数,实现排序.

leetcode 252

在这里插入图片描述

type timeArray [][]int
func (T timeArray) Len() int {
    return len(T)
}
func (T timeArray) Less(i, j int) bool {
    if T[i][0] < T[j][0] {
        return true
    } else if T[i][0] == T[j][0] {
        if T[i][1] > T[j][1] {
            return true
        } 
        return false
    } 
    return false
}
func (T timeArray) Swap(i, j int) {
    T[i], T[j] = T[j], T[i]
}

func canAttendMeetings(intervals timeArray) bool {
    size := len(intervals)
    if size < 2 {
        return true
    }

    sort.Sort(intervals)
    for i := 0; i < size - 1; i++ {
        if intervals[i][1] > intervals[i + 1][0] {
            return false
        }
    }
    return true
}

Eg :读和写

io 包提供了用于读和写的接口 io.Reader 和 io.Writer:

type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

“模板”

Go是动态语言,比C++的模板更灵活,

空接口

可以给一个空接口类型的变量 var val interface {} 赋任何类型的值。

	var x interface{}		// x为接口类型
	s := "WD"
	x = s					// 绑定string类型
    y,ok := x.(int)
    z,ok1 := x.(string)		// 判断类型,为string
    
    fmt.Println(y,ok)		// 0 false
    fmt.Println(z,ok1)		// WD true

空接口+type-switch+lambda

package main

import "fmt"

type specialString string

var whatIsThis specialString = "hello"

func TypeSwitch() {
	testFunc := func(any interface{}) {
		switch v := any.(type) {
		case bool:
			fmt.Printf("any %v is a bool type", v)
		case int:
			fmt.Printf("any %v is an int type", v)
		case float32:
			fmt.Printf("any %v is a float32 type", v)
		case string:
			fmt.Printf("any %v is a string type", v)
		case specialString:
			fmt.Printf("any %v is a special String!", v)
		default:
			fmt.Println("unknown type!")
		}
	}
	testFunc(whatIsThis)
}

func main() {
	TypeSwitch()
}

tuple实现

type Element interface{}

type Tuple []Element

func (p *Tuple) At(i int) Element {
	return (*p)[i]
}

func (p *Tuple) Set(i int, e Element) {
	(*p)[i] = e
}
 
func main() {
	tuple := Tuple{"1", 2, "abc", 3.1}
	fmt.Println(tuple)		// [1 2 abc 3.1]
}

复制数据切片至空接口切片

它们俩在内存中的布局是不一样的,必须一个个复制

var dataSlice []myType = FuncReturnSlice()
var interfaceSlice []interface{} = make([]interface{}, len(dataSlice))
for i, d := range dataSlice {
    interfaceSlice[i] = d
}

Eg:二叉树

package main

import "fmt"

type Node struct {
	le   *Node
	data interface{}
	ri   *Node
}

func NewNode(left, right *Node) *Node {
	return &Node{left, nil, right}
}

func (n *Node) SetData(data interface{}) {
	n.data = data
}

func main() {
	root := NewNode(nil, nil)
	root.SetData("root node")
	// make child (leaf) nodes:
	a := NewNode(nil, nil)
	a.SetData("left node")
	b := NewNode(nil, nil)
	b.SetData("right node")
	root.le = a
	root.ri = b
	fmt.Printf("%v\n", root) // Output: &{0x125275f0 root node 0x125275e0}
}

接口A《-》接口B

只有接口A的底层结构定义了接口B里的方法,那么就可以通过断言转换


package main
 
import "fmt"

type AInterface interface {
	A()
}
type BInterface interface {
	B()
}

type Object struct {
	a, b int
}

func (o *Object) A() {
	fmt.Println("A()")
}

func (o *Object) B() {
	fmt.Println("B()")
}
func main() {
	o := &Object{1, 2}
	var ai AInterface = o 
	ai.A()
	bi, ok := ai.(BInterface)	// AInterface->BInterface
	bi.B()
	fmt.Println(ok)				// true
}

反射

一、理解变量的内在机制

1.类型信息,元信息,是预先定义好的,静态的。

2.值信息,程序进行过程中,动态变化的。

二、反射和空接口

1.空接口相当于一个容器,能接受任何东西。

2.那怎么判断空接口变量存储的是什么类型呢?之前有使用过类型断言,这只是一个比较基础的方法

3.如果想获取存储变量的类型信息和值信息就要使用反射机制,所以反射是什么? 反射就是动态的获取变量类型信息和值信息的机制

三、怎么利用反射分析空接口里面的信息呢?

①首先利用的是GO语言里面的Reflect包

②利用包里的TypeOf方法可以获取 反射类型reflect.Type对象
利用包里的ValueOf方法可以获取 反射类型reflect.Value对象

func TypeOf(i interface{}) Type
func ValueOf(i interface{}) Value

1) 从reflect.Value对象中获取值的方法

在这里插入图片描述

package main
 
import (
	"fmt"
	"reflect"
)

func main() {
	var a int = 1024

	// 获取变量 a 的反射值对象,类型为 reflect.Value
	valueOfA := reflect.ValueOf(a)

	// 将 valueOfA 反射值对象以 interface{} 类型取出,通过类型断言转换为 int 类型并赋值给 getA
	var getA int = valueOfA.Interface().(int)
	
    // 获取64位的值, 强制类型转换为int类型
	var getA2 int = int(valueOfA.Int())
	
	fmt.Println(valueOfA, getA, getA2)	// 1024 1024 1024
}

2.从reflect.Type对象中获取各种信息

type Type interface {
    Align() int                 // 此类型的变量对齐后所占用的字节数
    FieldAlign() int            // 如果是struct的字段,对齐后占用的字节数
    Method(int) Method          // 返回类型方法集里的第 `i` (传入的参数)个方法
    MethodByName(string) (Method, bool) // 通过名称获取方法
    NumMethod() int             // 获取类型方法集里导出的方法个数
    Name() string               // 类型名称
    PkgPath() string            // 返回类型所在的路径,如:encoding/base64
    Size() uintptr               // 返回类型的大小,和 unsafe.Sizeof 功能类似
    String() string                     // 返回类型的字符串表示形式
    Kind() Kind                         // 返回类型的类型值
    Implements(u Type) bool             // 类型是否实现了接口 u
    AssignableTo(u Type) bool           // 是否可以赋值给 u
    ConvertibleTo(u Type) bool          // 是否可以类型转换成 u
    Comparable() bool                   // 类型是否可以比较
    // 下面这些函数只有特定类型可以调用
    // 如:Key, Elem 两个方法就只能是 Map 类型才能调用
    Bits() int        // 类型所占据的位数
    ChanDir() ChanDir // 返回通道的方向,只能是 chan 类型调用
    // 返回类型是否是可变参数,只能是 func 类型调用
    // 比如 t 是类型 func(x int, y ... float64)
    // 那么 t.IsVariadic() == true
    IsVariadic() bool
    Elem() Type // 返回内部子元素类型,只能由类型 Array, Chan, Map, Ptr, or Slice 调用

    // 返回结构体类型的第 i 个字段,只能是结构体类型调用
    // 如果 i 超过了总字段数,就会 panic
    Field(i int) StructField
    FieldByIndex(index []int) StructField        // 返回嵌套的结构体的字段
    FieldByName(name string) (StructField, bool) // 通过字段名称获取字段
    // FieldByNameFunc returns the struct field with a name
    // 返回名称符合 func 函数的字段
    FieldByNameFunc(match func(string) bool) (StructField, bool)
    In(i int) Type           // 获取函数类型的第 i 个参数的类型
    Key() Type               // 返回 map 的 key 类型,只能由类型 map 调用
    Len() int                // 返回 Array 的长度,只能由类型 Array 调用
    NumField() int           // 返回类型字段的数量,只能由类型 Struct 调用
    NumIn() int              // 返回函数类型的输入参数个数
    NumOut() int             // 返回函数类型的返回值个数
    Out(i int) Type          // 返回函数类型的第 i 个值的类型
    common() *rtype          // 返回类型结构体的相同部分
    uncommon() *uncommonType // 返回类型结构体的不同部分
}

Kind() 获取类型

reflect.Type和reflect.Value类型都有Kind()方法,返回底层数据类型

Elem() 通过反射修改值

v := reflect.ValueOf(x) 函数通过传递一个 x 拷贝创建了 v,那么 v 的改变并不能更改原始的 x。
要想 v 的更改能作用到 x,那就必须传递 x 的地址 v = reflect.ValueOf(&x)


package main
 
import (
	"fmt"
	"reflect"
)

func main() {
	var x float64 = 3.4
	v := reflect.ValueOf(x)

	// 1. CanSet看一看能不能设置了
	fmt.Println(v.CanSet())		// false

	// 2. 不能是因为ValueOf方法创建的反射对象为传值,必须传地址才能设置
	vp := reflect.ValueOf(&x)
	fmt.Println(vp.CanSet())	// false

	// 3. 使用Elem导出
	vp = vp.Elem()
	fmt.Println(vp.CanSet())	// true

	// 4. 设置
	vp.SetFloat(3.1415)
	fmt.Println(vp)
}

NumField(), Field() 获取结构体字段

type Test struct {
	A int 
	B string
}
func main() {
	t := Test{22, "aaaa"}
	elem := reflect.ValueOf(&t).Elem()
	typeOfT := reflect.TypeOf(t)

	for i := 0; i < typeOfT.NumField(); i++ {
		fieldValue := elem.Field(i)
		fieldType := typeOfT.Field(i)
		fmt.Printf("%d: %s %s = %v\n", i,
			fieldType.Name, fieldValue.Type(), fieldValue)
	}
}

在这里插入图片描述

Printf 和反射

库中很多函数都使用反射来分析参数

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

总结:Go 中的面向对象

我们总结一下前面看到的:Go 没有类,而是松耦合的类型、方法对接口的实现。

OO 语言最重要的三个方面分别是:封装,继承和多态,在 Go 中它们是怎样表现的呢?

封装(数据隐藏):

1)包范围内的:通过标识符首字母小写,对象 只在它所在的包内可见

2)可导出的:通过标识符首字母大写,对象 对所在包以外也可见

类型只拥有自己所在包中定义的方法。

继承:用组合实现:内嵌一个(或多个)包含想要的行为(字段和方法)的类型;多重继承可以通过内嵌多个类型实现

多态:用接口实现:某个类型的实例可以赋给它所实现的任意接口类型的变量。类型和接口是松耦合的,并且多重继承可以通过实现多个接口实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值