Java 葛朗:当你有多重继承时,接口有什么意义

Java 葛朗:当你有多重继承时,接口有什么意义,java,inheritance,interface,go,multiple-inheritance,Java,Inheritance,Interface,Go,Multiple Inheritance,我是一名Java程序员,正在学习用围棋编程。到目前为止,我真的很喜欢这种语言。比Java多得多 但有一件事我有点困惑。Java有接口,因为类只能从一个类继承。既然Go允许多重继承,那么接口又有什么意义呢?如果supertype X是一个接口,那么维护代码的人马上就会知道它没有方法实现。如果超类型Y是一个抽象类,那么维护代码的人必须检查是否存在方法实现。因此,这是一个文档/维护/可读性问题。如果supertype X是一个接口,那么维护代码的人马上就会知道它没有方法实现。如果超类型Y是一个抽象类,

我是一名Java程序员,正在学习用围棋编程。到目前为止,我真的很喜欢这种语言。比Java多得多


但有一件事我有点困惑。Java有接口,因为类只能从一个类继承。既然Go允许多重继承,那么接口又有什么意义呢?

如果supertype X是一个接口,那么维护代码的人马上就会知道它没有方法实现。如果超类型Y是一个抽象类,那么维护代码的人必须检查是否存在方法实现。因此,这是一个文档/维护/可读性问题。

如果supertype X是一个接口,那么维护代码的人马上就会知道它没有方法实现。如果超类型Y是一个抽象类,那么维护代码的人必须检查是否存在方法实现。因此,它是一个文档/维护/可读性的东西。

类可以从多个类文件继承和实现

除非我误解了:

public class MyClass extends MySuperClass implements MyInterface, MySecondInterface
接口的要点是允许一个完全抽象的类。如此抽象以至于没有定义一个方法。 当我需要创建几个具有相同基本结构的抽象类时,我会使用一个接口。然后,我将能够创建扩展抽象类的类的实例,而抽象类又将实现接口


这是通过接口java.util.Collection完成的,一些类(如java.util.ArrayList和java.util.Stack)实现了该接口。通过这种方式,您可以在集合中存储各种列表项。这就是为什么ArrayList有一个addAllCollection方法类可以从多个类文件继承和实现

除非我误解了:

public class MyClass extends MySuperClass implements MyInterface, MySecondInterface
接口的要点是允许一个完全抽象的类。如此抽象以至于没有定义一个方法。 当我需要创建几个具有相同基本结构的抽象类时,我会使用一个接口。然后,我将能够创建扩展抽象类的类的实例,而抽象类又将实现接口

这是通过接口java.util.Collection完成的,一些类(如java.util.ArrayList和java.util.Stack)实现了该接口。通过这种方式,您可以在集合中存储各种列表项。这就是为什么ArrayList有一个添加AllCollection多态性的方法

接口使函数具有“占位符”参数,该参数可以将不同的结构作为参数。例如,如果结构Man、Woman、Child实现接口Human,那么参数为Human的方法可以将结构Man、Woman、Child中的任何一个作为参数。因此,只要接口参数实现接口中定义的所有函数,它就可以“变形”为作为参数传递的任何结构

这很重要,因为接口是在Go中实现多态性的唯一方法,因为它没有继承性。所以,如果Man通过将Human作为匿名字段来“扩展”Human,那么任何使用Human作为参数的方法都不能将Man作为参数

我的困惑源于这样一个事实:继承也是Java中实现多态性的一种方式,我假设这里也是这样。我站直了

多态性

接口使函数具有“占位符”参数,该参数可以将不同的结构作为参数。例如,如果结构Man、Woman、Child实现接口Human,那么参数为Human的方法可以将结构Man、Woman、Child中的任何一个作为参数。因此,只要接口参数实现接口中定义的所有函数,它就可以“变形”为作为参数传递的任何结构

这很重要,因为接口是在Go中实现多态性的唯一方法,因为它没有继承性。所以,如果Man通过将Human作为匿名字段来“扩展”Human,那么任何使用Human作为参数的方法都不能将Man作为参数


我的困惑源于这样一个事实:继承也是Java中实现多态性的一种方式,我假设这里也是这样。我站直了

Go中的接口与Java中的接口非常不同

在Java中,类必须正式同意实现接口:

public class Foo implements iFoo
type MyInterface interface {
    io.Reader // Include Reader interface
    Seek(int) error // Include my own function
}
func DoSomething(r MyInterface) {
    buf := make([]byte, 128)
    n, err := r.Read(buf)
    ...
}
在Go中,用户类型通过简单地实现接口

然后,函数或属性可以定义期望的内容:

func DoSomething(r io.Reader) {
    buf := make([]byte, 128)
    n, err := r.Read(buf)
    ...
}
DoSomething函数可以传递任何实现io.Reader接口中的Read函数的内容,而不需要知道或关心接口。调用者有责任确保它正在传递实现接口的东西。这是在编译时检查的

我们可以更进一步。我们可以定义自己的接口:

public class Foo implements iFoo
type MyInterface interface {
    io.Reader // Include Reader interface
    Seek(int) error // Include my own function
}
func DoSomething(r MyInterface) {
    buf := make([]byte, 128)
    n, err := r.Read(buf)
    ...
}
Go的另一个不同之处在于它没有类或对象类型。 任何用户声明的类型,无论是基于整数、字符串、结构、数组、切片、通道等,都可以附加方法

Go也没有您通常习惯的典型类继承,但它确实有一些非常接近的特性

重新声明的类型:

type Num int

func (n Num) Print() {
    print(n)
}

type Number Num

func (n Number) Print() {
    Num(n).Print()
}
匿名字段:

type Foo struct {
    sync.Mutex
}

func main() {
    f := Foo{}
    f.Lock()
    // ...
    f.Unlock()
}

Go中的接口与Java中的接口非常不同

在Java中,类必须正式同意实现接口:

public class Foo implements iFoo
type MyInterface interface {
    io.Reader // Include Reader interface
    Seek(int) error // Include my own function
}
func DoSomething(r MyInterface) {
    buf := make([]byte, 128)
    n, err := r.Read(buf)
    ...
}
在Go中,用户类型通过简单地实现接口

然后,函数或属性可以定义期望的内容:

func DoSomething(r io.Reader) {
    buf := make([]byte, 128)
    n, err := r.Read(buf)
    ...
}
DoSomething函数可以传递任何实现io.Reader接口中的Read函数的内容,而不需要知道或关心接口。调用者有责任确保它正在传递实现接口的东西。这是在编译时检查的

我们可以更进一步。我们可以定义自己的接口:

public class Foo implements iFoo
type MyInterface interface {
    io.Reader // Include Reader interface
    Seek(int) error // Include my own function
}
func DoSomething(r MyInterface) {
    buf := make([]byte, 128)
    n, err := r.Read(buf)
    ...
}
Go的另一个不同之处在于它没有类或对象类型。任何用户声明的类型,无论是基于整数、字符串、结构、数组、切片、通道等,都可以附加方法

Go也没有您通常习惯的典型类继承,但它确实有一些非常接近的特性

重新声明的类型:

type Num int

func (n Num) Print() {
    print(n)
}

type Number Num

func (n Number) Print() {
    Num(n).Print()
}
匿名字段:

type Foo struct {
    sync.Mutex
}

func main() {
    f := Foo{}
    f.Lock()
    // ...
    f.Unlock()
}

依我拙见,这个问题的答案也适用于这里。接口和多重继承是相关的,但只是相切的。Java的接口不能替代多重继承。接口允许您将契约与实现分离。多重继承的用途有限,但会带来更多的麻烦和非直观的、难以调试的问题,IMHO。@JimGarrison,你能详细说明一下吗?接口只是告诉“做什么”,而通过继承,您还可以通过覆盖该行为的选项获得关于“如何”做的信息。继承如何会带来麻烦,在什么情况下接口会更好。举个例子真的很有帮助!许多工作已经投入到解决问题的工作中。Java完全避免了这一点。一篇关于接口和继承之间区别的完整论文将占用更多的空间,因此,完全同意你的观点。Java避免了多重继承,因此需要接口。Go通过使您重写冲突的方法来启用多重继承。我的问题是接口在这个场景中的作用。原来是鸭子打字。我的拙见是,这个问题的答案也适用于这里。接口和多重继承是相关的,但只是相切的。Java的接口不能替代多重继承。接口允许您将契约与实现分离。多重继承的用途有限,但会带来更多的麻烦和非直观的、难以调试的问题,IMHO。@JimGarrison,你能详细说明一下吗?接口只是告诉“做什么”,而通过继承,您还可以通过覆盖该行为的选项获得关于“如何”做的信息。继承如何会带来麻烦,在什么情况下接口会更好。举个例子真的很有帮助!许多工作已经投入到解决问题的工作中。Java完全避免了这一点。一篇关于接口和继承之间区别的完整论文将占用更多的空间,因此,完全同意你的观点。Java避免了多重继承,因此需要接口。Go通过使您重写冲突的方法来启用多重继承。我的问题是接口在这个场景中的作用。原来是鸭子打字。我正在为同样的问题添加一个答案