Привет! Я Го, и я ненавижу, когда меня называют Голангом. Я функциональный, императивный, параллельный и объектно-ориентированный язык программирования. В этой статье я расскажу вам, как я реализую классические объектно-ориентированные конструкции, такие как структуры и методы.

Структура

Структура — это совокупный тип данных, который я предоставляю программистам, чтобы они могли группировать вместе ноль или более именованных значений произвольного типа в виде единой сущности. Ниже приведены некоторые из способов, которыми я разрешаю объявлять структуры.

type Point struct {
    X  int
    Y  int
}
// or
type Point struct {X, Y int}

И для создания переменной или указателя указанного выше типа структуры синтаксис выглядит следующим образом.

var point Point // Declares a varible of type Point
var pointp *Point // Declares a pointer of type Point

Как для переменных, так и для указателей я разрешаю доступ к полю с использованием записи через точку.

point.X = 4 
pointp.X = 4

Последнее утверждение эквивалентно

(*pointp).X = 4

Я разрешаю инициализацию структуры одним из двух способов. Вы можете либо указать значение для каждого поля в правильном порядке, либо перечислить некоторые или все имена полей и соответствующие им значения в любом порядке.

p := Point{1, 2}// X and Y get assigned 1 and 2 respectively
// or
p := Point{Y: 1}// Y gets assigned 1 and X is assigned its nil value

Есть кое-что в структурах, которые я разрешаю, а именно сравнение структур. Но это возможно, только если все поля структуры сопоставимы.

p := Point{1, 2}
q := Point{1, 2}
r := Point{X:2, Y:3}
p == q   // true
q == r   // false
p == r   // false

Что касается наследования, то я его не разрешаю, но то, что я разрешаю вместо этого, называется встраиванием структуры . Несмотря на то, что это может выглядеть и ощущаться как наследование, между ними есть существенная разница, которая станет очевидной при обсуждении интерфейсов. Позвольте мне объяснить встраивание, приведя пример.

type Circle struct {
    Point
    Radius int
}
var c Circle
c.X = 0
c.Y = 0
c.Radius = 5

Итак, встраивая, я разрешаю доступ к полям структуры Point внутри структуры Circle. Но я не разрешаю этого при инициализации структур.

c := Circle{X: 0, Y: 0, Radius: 5} // compile error
c := Circle{Point{X:0, Y:0}, 5} // compiles successfully

Методы

Я разрешаю объявление метода так же, как объявление функции с дополнительным параметром перед именем функции. Параметр связывает функцию с типом этого параметра. Напишем метод и прикрепим его к объектам типа Point.

func (p Point) Distance (q Point) {
    return math.Hypot(q.X - p.X, q.Y - p.Y)
}

Дополнительный параметр p называется получателем метода. Я не использую специальное имя, такое как этот или я, для получателя. Я разрешаю выбирать имена получателей так же, как и любой другой параметр. Получатель также может быть указателем на переменную.

func (p *Point) ScaleBy (factor float64) {
    p.X *= factor
    p.Y *= factor
}

Для меня nil является допустимым получателем, если его значение nil имеет смысл. Как и в случае связанного списка целых чисел, где значение nil означает пустой список.

// An IntList is a linked list of integers.
// A nil *IntList represents an empty list.
type IntList struct {
    Value int
    Tail *IntList
}
//Sum returns the sum of the list elements.
func (list *IntList) Sum() {
    if list == nil {
        return 0
    }
    return list.Value + list.Tail.Sum()
}

Я надеюсь, что все, что мы обсудили до сих пор, имеет смысл и побудит вас писать код на Go. В следующей серии статей об интерфейсах, горутинах, каналах и параллелизме будет больше информации.