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

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

报告相同问题?

悬赏问题

  • ¥15 微信会员卡等级和折扣规则
  • ¥15 微信公众平台自制会员卡可以通过收款码收款码收款进行自动积分吗
  • ¥15 随身WiFi网络灯亮但是没有网络,如何解决?
  • ¥15 gdf格式的脑电数据如何处理matlab
  • ¥20 重新写的代码替换了之后运行hbuliderx就这样了
  • ¥100 监控抖音用户作品更新可以微信公众号提醒
  • ¥15 UE5 如何可以不渲染HDRIBackdrop背景
  • ¥70 2048小游戏毕设项目
  • ¥20 mysql架构,按照姓名分表
  • ¥15 MATLAB实现区间[a,b]上的Gauss-Legendre积分