Метод выглядит как обычное определение функции, за исключением того, что у него есть получатель. Получатель аналогичен ссылке this в методе экземпляра Java.
type MyType struct { i int }
func (p *MyType) Get() int {
return p.i
}
var pm = new(MyType)
var n = pm.Get()
Это объявляет метод Get, связанный с MyType. Получатель назван p в теле функции.
Методы объявлены для определенных типов. Если вы преобразовываете значение в другой тип, новое значение будет иметь методы нового типа, а не методы старого типа.
Вы можете определить методы для встроенного типа, объявив новый определенный тип, производный от него. Новый тип отличается от встроенного.
type MyInt int
func (p MyInt) Get() int {
return int(p) // Требуется преобразование.
}
func f(i int) {}
var v MyInt
v = v * v // Операторы базового типа все еще применяются.
f(int(v)) // int(v) не имеет объявленных методов.
f(v) // INVALID
Интерфейсы
Интерфейс Go похож на интерфейс Java, но любой тип, который предоставляет методы, названные в интерфейсе Go, можно рассматривать как реализацию этого интерфейса. Никакого явного объявления не требуется.
Предположим, что этот интерфейс определен:
type MyInterface interface {
Get() int
Set(i int)
}
Поскольку MyType уже имеет метод Get, мы можем заставить MyType удовлетворить интерфейс, добавив
func (p *MyType) Set(i int) {
p.i = i
}
Теперь любая функция, которая принимает MyInterface в качестве параметра, будет принимать переменную типа *MyType.
func GetAndSet(x MyInterface) {}
func f1() {
var p MyType
GetAndSet(&p)
}
В терминах Java определение Set и Get для *MyType привело к тому, что *MyType автоматически реализует MyInterface. Тип может удовлетворять нескольким интерфейсам. Это форма неявной типизации (утиная типизация (англ. Duck typing)).
Когда я вижу птицу, которая ходит, как утка, плавает, как утка, и крякает, как утка, я называю эту птицу уткой. - Джеймс Уиткомб Райли
Встраивание (делегирование)
Встраивание типа как анонимного поля может использоваться для реализации формы подтипов.
type MySubType struct {
MyType
j int
}
func (p *MySubType) Get() int {
p.j++
return p.MyType.Get()
}
Это реализация MySubType как подтипа MyType.
func f2() {
var p MySubType
GetAndSet(&p)
}
Метод Set унаследован от MyType, поскольку методы, связанные с анонимным полем, повышаются до методов включающего типа.
В этом случае, поскольку MySubType имеет анонимное поле типа MyType, методы MyType также становятся методами MySubType. Метод Get был переопределен, а метод Set был унаследован.
Это не то же самое, что подкласс в Java, а форма делегирования. Когда вызывается метод анонимного поля, его получателем является поле, а не окружающая структура. Другими словами, методы анонимных полей не отправляются динамически. Если вам нужен эквивалент динамического поиска метода Java, используйте интерфейс.
func f3() {
var v MyInterface
v = new(MyType)
v.Get() // Вызов метода Get для *MyType.
v = new(MySubType)
v.Get() // Вызов метода Get для *MySubType.
}
Утверждения типа
Переменная, имеющая тип интерфейса, может быть преобразована в другой тип интерфейса с помощью утверждения типа. Это реализуется динамически во время выполнения. В отличие от Java, между двумя интерфейсами не требуется декларирования взаимосвязи.
type Printer interface {
Print()
}
func f4(x MyInterface) {
x.(Printer).Print() // утверждения типа для Printer
}
Преобразование в Printer полностью динамическое. Оно будет работать до тех пор, пока динамический тип x (фактический тип значения, хранящегося в x) определяет метод Print.
Читайте также:
- Go для Java разработчиков: Hello world пример
- Go для Java разработчиков: основные отличия Go
- Go для Java разработчиков: синтаксис
- Go для Java разработчиков: константы, структуры, указатели
- Go для Java разработчиков: срезы, создание значений
- Основы Go: методы
Комментариев нет:
Отправить комментарий