Go语言接口与组合

207 阅读5分钟

"Go vs Java:接口的新视角" — 这篇文章带你走进一个不同寻常的编程世界,通过比较 Go 语言和 Java 中的接口,揭示了 Go 的革命性简洁和 Java 的经典严谨之间的巧妙平衡。我们将探讨 Go 接口的隐式实现如何带来代码的高度解耦,以及 Java 接口的显式声明如何提供结构化的严密性。这不仅是一次语言特性的比较,更是一场编程范式和设计思维的碰撞。无论你是对 Go 充满好奇,还是 Java 的忠实粉丝,这篇文章都将为你打开一个全新的视角。

Go接口

在Go语言中,接口(interface)是一种抽象类型,定义了一组方法的集合。一个对象只要实现了接口定义的所有方法,就被认为是实现了该接口。接口提供了一种实现多态的机制,使得不同类型的对象能够以统一的方式被处理。

下面的例子中,Shape是一个接口,定义了一个Area方法。Circle和Rectangle分别是两个实现了Shape接口的结构体。在main函数中,通过创建Circle和Rectangle对象,并将它们放入一个Shape类型的切片中,实现了对不同类型对象的统一处理。

package main

import "fmt"

// 定义一个接口
type Shape interface {
    Area() float64
}

// 定义一个结构体
type Circle struct {
    Radius float64
}

// 实现接口方法
func (c Circle) Area() float64 {
    return 3.14 * c.Radius * c.Radius
}

// 定义另一个结构体
type Rectangle struct {
    Width  float64
    Height float64
}

// 实现接口方法
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

func main() {
    // 创建一个圆形对象
    circle := Circle{Radius: 5}

    // 创建一个矩形对象
    rectangle := Rectangle{Width: 4, Height: 6}

    // 使用接口进行统一处理
    shapes := []Shape{circle, rectangle}

    // 计算所有形状的面积
    for _, shape := range shapes {
        fmt.Println("Area:", shape.Area())
    }
}

go接口 VS java接口

  • 语法差异

  • Golang: 在Go中,接口是一组方法的集合,类型通过实现这些方法来满足接口。接口的声明非常简单,只需指定方法的签名。

    type MyInterface interface {    MyMethod(int) int } 
    
  • Java: Java中的接口使用interface关键字声明,接口中定义了抽象方法。类通过使用implements关键字来实现接口。

    interface MyInterface {    int MyMethod(int num); }
    
  • 接口值

  • Golang: Golang中的接口是值类型,接口值可以包含任何实现了接口的具体类型的实例。

  • Java: Java中的接口是引用类型,接口变量引用实现了该接口的对象。

  • 隐式接口 vs 显示接口

  • Golang: Golang的接口是隐式的,类型无需显式声明实现某个接口,若实现了接口中的所有方法,该类型才被认为实现了该接口,否则不算实现了某一个接口。请结合代码理解。

package main

import "fmt"

type MyInterface interface {
	Method1() int
	Method2(MyInterface)
}

// 结构体1 实现 MyInterface 一个接口
type Struct1 struct {
}

func (s *Struct1) Method1() int {
	return 42
}

// 结构体2 实现 MyInterface 全部接口
type Struct2 struct {
}

func (s *Struct2) Method2(myInterface MyInterface)  {
	fmt.Println("Struct2 Method2")
	myInterface.Method1()
}

func (s *Struct2) Method1() int {
	fmt.Println("Struct2 Method1")
	return 42
}

func main() {
    // 定义接口 变量
	var myInterface MyInterface

	// myInterface = &Struct1{} // 编译错误,Struct1没有实现Method2方法
    // 编译报错: cannot use &Struct1{} (value of type *Struct1) as MyInterface value in assignment: *Struct1 does not implement MyInterface (missing method Method2)

    // 正常运行
	myInterface = &Struct2{}
	myInterface.Method2(myInterface)	
}
  • Java: 在Java中,类必须显式地声明实现接口,使用关键字implements。

  • 多态

  • Golang: Golang通过隐式接口实现多态,允许不同类型的对象以相同的方式被处理。

  • Java: Java通过接口和抽象类实现多态,允许不同类的对象以相同的方式被处理。

  • 接口组合

  • Golang: Golang允许通过嵌入接口的方式进行接口组合。

  • Java: Java不支持接口的嵌套,但可以通过在类中实现多个接口来达到类似的效果。

go语言是否拥有Java的抽象类

在Go语言中,没有与Java中的抽象类直接相对应的概念。Go的类型系统不支持类和继承,因此也就没有抽象类的概念。不过,Go通过接口和组合来提供类似的功能。

场景:一个动物的抽象类,基础属性有名字、声音,方法有动物的两个行为

Java抽象类实现

// 动物的抽象
public abstract class Animal {

    protected String name;
    protected String sound;

    public Animal(String name, String sound) {
        this.name = name;
        this.sound = sound;
    }

    public void makeSound() {
        System.out.println(name + "大叫" +sound);
    }

    public abstract void play();
}

// 具体动物
public class Dog extends Animal{

    public Dog(String name, String sound) {
        super(name, sound);
    }

    @Override
    public void play() {
        System.out.println("小狗" + name + "喜欢跑步!");
    }
}

// 具体动物
public class Cat extends Animal{
    public Cat(String name, String sound) {
        super(name, sound);
    }

    @Override
    public void play() {
        System.out.println("小猫" + name + "喜欢睡觉~");
    }
}

// 实现
public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog("🐕", "汪汪汪!");
        Animal cat = new Cat("🐱", "喵喵喵~");
    	// dog的行为
        dog.makeSound();
        dog.play();

        // cat的行为
        cat.makeSound();
        cat.play();
    }
}

go接口和组合实现

package main

import "fmt"

// Animal 接口
type Animal interface {
    MakeSound()
    Play()
}

// BaseAnimal 提供了Animal的一部分实现
type BaseAnimal struct {
    Name  string
    Sound string
}

// MakeSound 是 BaseAnimal 的方法
func (b *BaseAnimal) MakeSound() {
    fmt.Printf("%s大叫%s\n", b.Name, b.Sound)
}

// Dog 结构体通过嵌入 BaseAnimal 组合了其行为,并实现 Animal 接口
type Dog struct {
    BaseAnimal
}

func (d *Dog) Play() {
    fmt.Printf("小狗%s喜欢跑步\n", d.Name)
}

// Cat 结构体通过嵌入 BaseAnimal 组合了其行为,并实现 Animal 接口
type Cat struct {
    BaseAnimal
}

func (c *Cat) Play() {
    fmt.Printf("小猫%s喜欢睡觉\n", c.Name)
}

func main() {
    var dog Animal
    var cat Animal
    dog = &Dog{BaseAnimal{Name: "🐕", Sound: "汪汪汪!"}}
    dog.MakeSound()
    dog.Play()

    cat = &Cat{BaseAnimal{Name: "🐱", Sound: "喵喵喵~"}}
    cat.MakeSound()
    cat.Play()

    fmt.Printf("Type: %T, Value: %v\n", dog, dog)
    fmt.Printf("Type: %T, Value: %v\n", cat, cat)
}

如果这篇文章对你有帮助,就点一个攒吧~