interface理解

interface(接口)是golang最重要的特性之一,实现多态。Interface类型可以定义一组方法,但是这些不需要实现。并且interface不能包含任何变量。

特点

  • interface 是方法或行为声明的集合
  • interface接口方式实现比较隐性,任何类型的对象实现interface所包含的全部方法,则表明该类型实现了该接口。
  • interface还可以作为一中通用的类型,其他类型变量可以给interface声明的变量赋值。
  • interface 可以作为一种数据类型,实现了该接口的任何对象都可以给对应的接口类型变量赋值。

底层实现

Go的interface源码在Golang源码的runtime目录中。
Go的interface是由两种类型来实现的:iface和eface。

两种接口

​runtime.iface

package main

import "fmt"

// Person 定义接口
type Person interface {
    GetName() string
    GetAge() uint32
}

// Student 定义类型
type Student struct {
    Name string
    Age uint32
}

func (s Student) GetName()  string{
    return s.Name
}
func (s Student) GetAge()  uint32{
    return s.Age
}

func main() {

var student Student
    student.Age = 12
    student.Name = "小明"

var person Person
    person = student  //接口执行向student
    fmt.Printf("name:%s,age: %d\n", person.GetName(), person.GetAge())
}

​内部结构

type iface struct {
    tab  *itab
    data unsafe.Pointer
}

itab源码

type itab struct {
    inter *interfacetype //此属性用于定位到具体interface
    _type *_type         //此属性用于定位到具type
    hash  uint32         // copy of _type.hash. Used for type switches.
    _     [4]byte
    fun   [1]uintptr     //方法集
}

代码例子 

package main

import "fmt"

// Person 定义接口
type Person interface {
    GetName() string
    GetAge() uint32
}

// Student 定义类型
type Student struct {
    Name string
    Age uint32
}

func (s Student) GetName()  string{
    return s.Name
}
func (s Student) GetAge()  uint32{
    return s.Age
}

func main() {

var student Student
    student.Age = 12
    student.Name = "小明"

var person Person
    person = student
    fmt.Printf("name:%s,age: %d\n", person.GetName(), person.GetAge())
}

 

runtime.eface​

使用​​runtime.eface​​​表示第二种不包含任何方法的接口,第二种在我们日常开发中经常使用到,所以在实现时使用了特殊的类型。从编译角度来看,golang并不支持泛型编程。但还是可以用​​interface{}​​ 来替换参数,而实现泛型。

空接口interface{}没有任何方法,所以所有类型都实现了空接口,即我们可以把任何一个变量赋给空接口类型。

var person interface{} = xxxx实体

 

侵入式与非侵入式的理解

侵入式
你的代码里已经嵌入了别的代码,这些代码可能是你引入过的框架,也可能是你通过接口继承得来的,比如:java中的继承,必须显示的表明我要继承那个接口,这样你就可以拥有侵入代码的一些功能。所以我们就称这段代码是侵入式代码。
优点:通过侵入代码与你的代码结合可以更好的利用侵入代码提供给的功能。
缺点:框架外代码就不能使用了,不利于代码复用。依赖太多重构代码太痛苦了。

非侵入式
正好与侵入式相反,你的代码没有引入别的包或框架,完完全全是自主开发。比如go中的接口,不需要显示的继承接口,只需要实现接口的所有方法就叫实现了该接口,即便该接口删掉了,也不会影响我,所有go语言的接口数非侵入式接口;再如Python所崇尚的鸭子类型。

优点:代码可复用,方便移植。非侵入式也体现了代码的设计原则:高内聚,低耦合。
缺点:无法复用框架提供的代码和功能。

java实现

public interface IPersonService {
    String getName();
    Integer getAge();
}
public class PersonService implements IPersonService{
    @Override
    public String getName() {
        return "小明";
    }
    @Override
    public Integer getAge() {
        return 12;
    }
}

go实现

package main
import "fmt"

// Person 定义接口
type Person interface {
    GetName() string
    GetAge() uint32
}
// Student 定义类型
type Student struct {
    Name string
    Age uint32
}

func (s Student) GetName()  string{
    return s.Name
}
func (s Student) GetAge()  uint32{
    return s.Age
}

func main() {

var student Student
    student.Age = 12
    student.Name = "小明"

var person Person
    person = student
    fmt.Printf("name:%s,age: %d\n", person.GetName(), person.GetAge())
}

区别

侵入式通过 implements 把实现类与具体接口绑定起来了,因此有了强耦合;
假如修改了接口方法,则实现类方法必须改动;
假如类想再实现一个接口,实现类也必须进行改动;
后续实现此接口的类,必须了解相关的接口;
Go语言非侵入式的方式很好地解决了这几个问题,只要实现了实现了与接口相同的方法,就实现了这个接口。随着代码量的增加,根本不需要的关心实现了哪些接口,不需要刻意去先定义接口再实现接口的固定模式,在原有类新增实现接口时,不需要更改类,做到低侵入式、低耦合开发的好处。

interface应用

类型转换

ok-idiom模式

type data int
  
func(d data)String()string{ 
   return fmt.Sprintf("data:%d",d) 
} 
  
func main() { 
   var d data=15
   var x interface{} =d
  
   if n,ok:=x.(fmt.Stringer);ok{  // 转换为更具体的接口类型 
       fmt.Println(n) 
    } 
  
   if d2,ok:=x.(data);ok{        // 转换回原始类型 
       fmt.Println(d2) 
    } 
  
   e:=x.(error)           // 错误:main.data is not error
   fmt.Println(e) 
}

siwch模式

func main() {
var x interface{} =func(x int)string{ 
       return fmt.Sprintf("d:%d",x) 
    } 
  
   switch v:=x.(type) {            // 局部变量v是类型转换后的结果 
   case nil: 
       println("nil") 
   case*int: 
       println(*v) 
   case func(int)string: 
       println(v(100)) 
   case fmt.Stringer: 
       fmt.Println(v) 
   default: 
       println("unknown") 
    } 
}

多态

......

参考

来源:

https://blog.51cto.com/u_14523732/5639864

https://blog.csdn.net/yuqiang870/article/details/124746693

posted @ 2023-08-13 22:22  意犹未尽  阅读(36)  评论(0编辑  收藏  举报