dongmei3869 2018-01-27 21:11
浏览 44
已采纳

非结构类型中的接口继承

I'm currently learning interfaces in Go, but I've gotten stuck with this piece of code:

package main

import (
    "fmt"
    "math"
)

// CommonMath is a common interface for math types
type CommonMath interface {
    Abs() float64
}

// Float64 is a custom float64 type
type Float64 float64

// Abs returns the modulus of objects implementing CommonMath
func (f Float64) Abs() float64 {
    if f < 0 {
        return -float64(f)
    }
    return float64(f)
}

// AbsSquared returns the square of objects implementing CommonMath
func AbsSquared(num CommonMath) float64 {
    return num.Abs() * num.Abs()
}

func main() {
    var f Float64
    f = -5

    type newF Float64
    var newFloat newF
    newFloat = 10.5

    fmt.Println(f)
    fmt.Println(f.Abs())
    fmt.Println(AbsSquared(newFloat))
}

As it turns out, it doesn't compile because newFloat does not implement the interface CommonMath. Being of type newF, which is a custom Float64 type that implements the interface, I don't know what's going on. To make things weirder, I substituted the declaration of newF and newFloat for the following, which implements the same but as a struct:

type newF struct {
    Float64
}
newFloat := newF{10.5}

Suddenly, the code builds perfectly fine. Does this mean that only structs can implement parent types' interfaces, and therefore declaring the type newF directly as Float64 is not allowed?

  • 写回答

1条回答 默认 最新

  • douhaoqiao9304 2018-01-27 21:35
    关注

    First, there is no inheritance in the Go , at all. There are several article across the web to discussing the design decision of Go discarding many feature of oop but that is really beyond this answer.

    Into your case. It is very clear that Float64 implements CommonMath. It has the method sets that CommonMath requires so it fits into the interface.

    However, when you declare another type, type newF Float64, the new type does not copy the method sets, only the data structure. This is specified in the spec:

    A type definition creates a new, distinct type with the same underlying type and operations as the given type, and binds an identifier to it.

    The new type is called a defined type. It is different from any other type, including the type it is created from.

    A defined type may have methods associated with it. It does not inherit any methods bound to the given type, but the method set of an interface type or of elements of a composite type remains unchanged:

    // A Mutex is a data type with two methods, Lock and Unlock.
    type Mutex struct         { /* Mutex fields */ }
    func (m *Mutex) Lock()    { /* Lock implementation */ }
    func (m *Mutex) Unlock()  { /* Unlock implementation */ }
    
    // NewMutex has the same composition as Mutex but its method set is empty.
    type NewMutex Mutex
    // The method set of the base type of PtrMutex remains unchanged,
    // but the method set of PtrMutex is empty.
    type PtrMutex *Mutex
    
    // The method set of *PrintableMutex contains the methods
    // Lock and Unlock bound to its embedded field Mutex.
    type PrintableMutex struct {
        Mutex
    }
    
    // MyBlock is an interface type that has the same method set as Block.
    type MyBlock Block
    

    And next come to type NewF struct { Float64 }. On a first look, this looks very much to inheritance but once again there is no such thing in Go. It is called embedding or composition. Again, the spec:

    A field declared with a type but no explicit field name is called an embedded field. An embedded field must be specified as a type name T or as a pointer to a non-interface type name *T, and T itself may not be a pointer type. The unqualified type name acts as the field name.

    A field or method f of an embedded field in a struct x is called promoted if x.f is a legal selector that denotes that field or method f.

    Promoted fields act like ordinary fields of a struct except that they cannot be used as field names in composite literals of the struct.

    Given a struct type S and a type named T, promoted methods are included in the method set of the struct as follows:

    • If S contains an embedded field T, the method sets of S and *S both include promoted methods with receiver T. The method set of *S also includes promoted methods with receiver *T.

    • If S contains an embedded field *T, the method sets of S and *S both include promoted methods with receiver T or *T.

    So, the trick here is promotion. The method of Float64 gets promoted to NewF. Again it may looks like inheritance in some way, but it is distinct. Please note that the promoted method still belongs to the embedded type, and the receiver will always be the orignal one. The following code print 4.

    package main
    
    import (
        "fmt"
    )
    
    type A struct {}
    
    type B struct {A}
    
    func (A) P() int {
        return 4
    }
    
    func (B) P() int {
        return 5
    }
    
    func (a A) S() {
        fmt.Println(a.P())
    }
    
    func main() {
        B{}.S()
    }
    

    playground: https://play.golang.org/p/BuBL69LqeY6

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥16 Qphython 用xlrd读取excel报错
  • ¥15 单片机学习顺序问题!!
  • ¥15 ikuai客户端多拨vpn,重启总是有个别重拨不上
  • ¥20 关于#anlogic#sdram#的问题,如何解决?(关键词-performance)
  • ¥15 相敏解调 matlab
  • ¥15 求lingo代码和思路
  • ¥15 公交车和无人机协同运输
  • ¥15 stm32代码移植没反应
  • ¥15 matlab基于pde算法图像修复,为什么只能对示例图像有效
  • ¥100 连续两帧图像高速减法