2016-10-22 3 views
2

Люди говорят: Go не является OO (объектно-ориентированным) языком; не используйте условия OO для Go. Хорошо, позвольте мне описать то, что я могу сделать с OO -Go "polymorphism"

С языком OO, я могу сделать разные животные говорят разные вещи в зависимости от их класса:

cat.Say() // miao 
sheep.Say() // bahh 
cow.Say() // moo 

То же, получающая Area() из фигур.

Однако this go demo code заставило меня поверить, что это невозможно. Ниже приведена Иллюстрация # 1.

Тогда сегодня я нашел this go demo code, что делает его вполне возможным. Ниже приведена Иллюстрация # 2.

Итак, мой вопрос в том, что принципиально отличается между двумя, что делает первый неправильным, а второй правильным? Как сделать первый «работает»?

Приложение # 1:

// Credits: hutch 
//   https://groups.google.com/d/msg/golang-nuts/N4MBApd09M8/0ij9yGHK_8EJ 
//////////////////////////////////////////////////////////////////////////// 

/* 

https://groups.google.com/d/msg/golang-nuts/N4MBApd09M8/tOO5ZXtwbhYJ 

LRN: 

Subtype polymorphism: Not applicable (Go doesn't have subtyping). 
Although if you embed a struct A implementing interface X into a struct B, 
struct B will implement interface X, and can be used instead of struct A in 
places where struct A is expected. So, kind of yes. 

Robert Johnstone: 

interfaces behave similarly to virtual functions, but they are not identical. See the (following) example program by hutch. 

*/ 

package main 

import "fmt" 

type A struct { 
    astring string 
} 

type B struct { 
    A 
    bstring string 
} 

type Funny interface { 
    strange() 
    str() string 
} 

func (this *A) strange() { 
    fmt.Printf("my string is %q\n", this.str()) 
} 

func (this *A) str() string { 
    return this.astring 
} 

func (this *B) str() string { 
    return this.bstring 
} 

func main() { 
    b := new(B) 
    b.A.astring = "this is an A string" 
    b.bstring = "this is a B string" 

    b.strange() 
    // Output: my string is "this is an A string" 

    // Many people familiar with OO (and unfamiliar with Go) will be quite 
    // surprised at the output of that program. 
} 

Приложение # 2:

// Credits: https://play.golang.org/p/Zn7TjiFQik 
//////////////////////////////////////////////////////////////////////////// 

/* 

Problem (From Polymorphism-Subtype.go): 

https://groups.google.com/d/msg/golang-nuts/N4MBApd09M8/tOO5ZXtwbhYJ 

LRN: Subtype polymorphism: Not applicable (Go doesn't have subtyping). 

Goal: 

This is to demo that "polymorphism" is still doable in Go. 

*/ 

package main 

import (
    "fmt" 
) 

type Shape interface { 
    Area() float32 
} 

type Point struct { 
    x float32 
    y float32 
} 

// Make sure the structs are different sizes so we're sure it'll work with 
// all sorts of types 
type Circle struct { 
    center Point 
    radius float32 
} 

func (c Circle) Area() float32 { 
    return 3.1415 * c.radius * c.radius 
} 

type Rectangle struct { 
    ul Point 
    lr Point 
} 

func (r Rectangle) Area() float32 { 
    xDiff := r.lr.x - r.ul.x 
    yDiff := r.ul.y - r.lr.y 
    return xDiff * yDiff 
} 

func main() { 
    mtDict := make(map[string]Shape) 
    // No problem storing different custom types in the multitype dict 
    mtDict["circ"] = Circle{Point{3.0, 3.0}, 2.0} 
    mtDict["rect"] = Rectangle{Point{2.0, 4.0}, Point{4.0, 2.0}} 

    for k, v := range mtDict { 
     fmt.Printf("[%v] [%0.2f]\n", k, v.Area()) 
    } 
} 

/* 

$ go run Polymorphism-Shape.go 
[circ] [12.57] 
[rect] [4.00] 

*/ 
+3

Разница в том, что функция вызывает интерфейс, а другая вызывает функцию типа. – tkausl

+0

Go не заставляет вас использовать OO, но на языке есть все, что нужно сделать OOP –

+0

Связанные/возможные дубликаты: [один] (http://stackoverflow.com/questions/29390736/go-embedded-struct-call-child -метод-вместо-родитель-метод); [Два] (http://stackoverflow.com/questions/30622605/can-embedded-struct-method-have-knowledge-of-parent-child); [Три] (http://stackoverflow.com/questions/29144622/what-is-the-idiomatic-way-in-go-to-create-a-complex-hierarchy-of-structs); [Четыре] (http://stackoverflow.com/questions/36710259/go-ensuring-embedded-structs-implement-interface-without-introducing-ambiguity). – icza

ответ

2

Ваши две выставки делают разные вещи.

В первом, B имеет A встроенный в него, и B не реализуют strange() метода самих, поэтому, когда вы звоните b.strange(), вы получаете реализацию strange(), определенную для A. Приемник() метода strange составляет b.A, а не b, поэтому печатается значение b.A.astring. Если вы хотите, чтобы strange печатать bstring, вам нужно будет определить strange для B.

Это указывает один из различий между Go и других объектно-ориентированных языков: вложение A внутри B не означает, что B является «подкласс» из A, поэтому объект типа B не может быть использован, когда объект типа A ожидается. Однако, поскольку B наследует поля и методы A, любой интерфейс, который реализуется A также осуществляется B, и, если эти методы не определены специально для B, они действуют на A в B, а не сам B.

Во втором экспорте у вас есть интерфейс Shape, который реализуется типами Circle и Rectangle. Тип элемента вашей карты - Shape, поэтому любой тип, реализующий этот интерфейс, может быть элементом на карте. При работе со значением типа интерфейса, которое вы выполняете в цикле, вы можете вызывать любой метод, определенный в интерфейсе для значения, и будет вызываться определение, соответствующее фактическому типу значения.

+0

Спасибо, объяснение мне очень понятно. – xpt

2

Прежде всего я хотел бы обсудить "невозможное" часть.

import "fmt" 

type Animal interface { 
    Say() string 
} 

type Cat struct {} 

func (cat Cat) Say() string { 
    return "miao" 
} 

type Sheep struct {} 

func (sheep Sheep) Say() string { 
    return "bahh" 
} 

type Cow struct {} 

func (cow Cow) Say() string { 
    return "moo" 
} 

func main() { 

    cat := Cat{} 
    sheep := Sheep{} 
    cow := Cow{} 

    fmt.Println(cat.Say()) 
    fmt.Println(sheep.Say()) 
    fmt.Println(cow.Say()) 
} 

Это будет работать точно так, как вы ожидали. Таким образом, существует полиморфизм с точки зрения «разных структур, которые по-разному реагируют на один и тот же метод».

Цель выставки # 1 показывает, что то, что Go делает, фактически аналогично Java castings перед @Overrides.

Просто добавьте следующий метод в первом примере и посмотрим, как это будет работать:

func (this B) strange() { 
    fmt.Printf("my string is %q\n", this.str()) 
}