[JAVA] Go 的继承

4145 0
黑夜隐士 2021-7-3 12:16:09 来自手机 | 显示全部楼层 |阅读模式

在这里插入图片描述

文章目录

1.前言

面向对象编程的三大特性:封装、继承、多态。可见继承是面向对象程序设计中一个重要的概念。Go 作为面向对象的编程语言,自然也支持继承。

比较特殊的是 Go 实现继承的方式与其他传统 OOP 语言所有不同,不像 C++ 有专门的继承语法,或者像 Java 中有专门的关键字 extends。

C++ 的继承:

// 基类
class Animal {
public:
    void eat(); 
	void sleep();
};


// 子类
class Dog : public Animal {
public:
    void bark();
};
[/code] 

Java 的继承:

// 基类
public class Animal {
	public void eat(){};
	public void sleep(){};
}

// 子类
public class Dog extends Animal {
	public void bark(){};
}
[/code] 

2.嵌入式继承机制

Go 使用匿名嵌套实现继承

我们用很容易理解的动物-猫来举例子。

type Animal struct {
	Name string
}

func (a *Animal) Eat() {
	fmt.Printf("%v is eating", a.Name)
	fmt.Println()
}

type Cat struct {
	Animal
}

cat := &Cat{
	Animal: Animal{
		Name: "cat",
	},
}
cat.Eat() // cat is eating
[/code] 

首先,我们实现了一个 Animal 的结构体,代表动物类。并声明了 Name 字段,用于描述动物的名字。

然后,实现了一个以 Animal 为 receiver 的 Eat 方法,来描述动物进食的行为。

最后,声明了一个 Cat 结构体,组合了 Cat 字段。再实例化一个猫,调用Eat方法,可以看到会正常的输出。

可以看到,Cat 结构体本身没有 Name 字段,也没有去实现 Eat() 方法。唯一有的就是匿名嵌套的方式继承了 Animal 父类,至此,我们证明了 Go 通过匿名嵌套的方式实现了继承。

上面是嵌入类型实例,同样地也可以嵌入类型指针。

type Cat struct {
	*Animal
}

cat := &Cat{
	Animal: &Animal{
		Name: "cat",
	},
}
[/code] 

3.嵌入式继承机制的的局限

相比于 C++ 和 Java, Go 的继承机制的作用是非常有限的,因为没有抽象方法,有很多的设计方案可以在 C++ 和 Java 中轻松实现,但是 Go 的继承却不能完成同样的工作。

package main

import "fmt"

// Animal 动物基类
type Animal struct {
	name string
}

func (a *Animal) Play() {
	fmt.Println(a.Speak())
}

func (a *Animal) Speak() string {
	return fmt.Sprintf("my name is %v", a.name)
}

func (a *Animal) Name() string {
	return a.name
}

// Dog 子类狗
type Dog struct {
	Animal
	Gender string
}

func (d *Dog) Speak() string {
	return fmt.Sprintf("%v and my gender is %v", d.Animal.Speak(), d.Gender)
}

func main() {
	d := Dog{
		Animal: Animal{name: "Hachiko"},
		Gender:  "male",
	}
	fmt.Println(d.Name())
	fmt.Println(d.Speak())
	d.Play() // Play() 中调用的是基类 Animal.Speak() 方法,而不是 Dog.Speak()
}
[/code] 

运行输出:

Hachiko
my name is Hachiko and my gender is male
my name is Hachiko
[/code] 

上面的例子中,Dog 类型重写了 Speak() 方法。然而如果父类型 Animal 有另外一个方法 Play() 调用 Speak() 方法,但是 Dog 没有重写 Play() 的时候,Dog 类型的 Speak() 方法则不会被调用,因为 Speak() 方法不是抽象方法,此时继承无法实现多态。

4.使用接口封装方法

为了解决上面的问题,我们应该使用接口封装方法,通过实现接口方法来实现多态。

package main

import (
    "fmt"
)

type Animal interface {
    Name() string
    Speak() string
    Play()
}

type Dog struct {
    name string
    gender string
}

func (d *Dog) Play() {
    fmt.Println(d.Speak())
}

func (d *Dog) Speak() string {
    return fmt.Sprintf("my name is %v and my gender is %v", d.name, d.gender)
}

func (d *Dog) Name() string {
    return d.name
}

func Play(a Animal) {
    a.Play()
}

func main() {
    d :=&Dog{"Hachiko", "male"}
    fmt.Println(d.Name())
    fmt.Println(d.Speak())
    Play(d)
}
[/code] 

运行输出:

Hachiko
my name is Hachiko and my gender is male
my name is Hachiko and my gender is male
[/code] 

注意:Go 中某个类型需要实现接口中的所有方法才算作实现了接口。

5.小结

如果一个 struct 嵌套了另一个匿名结构体,那么这个结构可以直接访问匿名结构体的属性和方法,从而实现继承。

如果一个 struct 嵌套了另一个有名的结构体,那么这个模式叫做组合。

如果一个 struct 嵌套了多个匿名结构体,那么这个结构可以直接访问多个匿名结构体的属性和方法,从而实现多重继承。

参考文献

[1] 掘金.两分钟让你明白Go中如何继承
[2] The Go Programming Language Specification.Struct types
[3] Hackthology.Golang中的面向对象继承
[4] Go语言中的代码重用 - 继承还是组合?


来源:https://blog.csdn.net/K346K346/article/details/117914689
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2025 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行