From c30beb9dc720a6505b7e980d5fca35d01da0a075 Mon Sep 17 00:00:00 2001 From: Andrey Krasavin Date: Fri, 22 Mar 2019 08:51:16 +0300 Subject: [PATCH] format code snippets like Go code & typos fixes --- ru/02.6.md | 562 ++++++++++++++++++++++++++--------------------------- 1 file changed, 281 insertions(+), 281 deletions(-) diff --git a/ru/02.6.md b/ru/02.6.md index 62a88c34..3209f201 100644 --- a/ru/02.6.md +++ b/ru/02.6.md @@ -19,70 +19,70 @@ ### Тип "Interface" Интерфейс определяет набор методов, поэтому, если тип реализует эти методы, говорится, что он реализует интерфейс. +```Go +type Human struct { + name string + age int + phone string +} - type Human struct { - name string - age int - phone string - } +type Student struct { + Human + school string + loan float32 +} - type Student struct { - Human - school string - loan float32 - } +type Employee struct { + Human + company string + money float32 +} - type Employee struct { - Human - company string - money float32 - } +func (h *Human) SayHi() { + fmt.Printf("Привет, я - %s, мой номер телефона - %s\n", h.name, h.phone) +} - func (h *Human) SayHi() { - fmt.Printf("Привет, я - %s, мой номер телефона - %s\n", h.name, h.phone) - } +func (h *Human) Sing(lyrics string) { + fmt.Println("Ля ля, ля ля ля, ля ля ля ля ля...", lyrics) +} - func (h *Human) Sing(lyrics string) { - fmt.Println("Ля ля, ля ля ля, ля ля ля ля ля...", lyrics) - } +func (h *Human) Guzzle(beerStein string) { + fmt.Println("Guzzle Guzzle Guzzle...", beerStein) +} - func (h *Human) Guzzle(beerStein string) { - fmt.Println("Guzzle Guzzle Guzzle...", beerStein) - } +// Employee перегружает метод SayHi +func (e *Employee) SayHi() { + fmt.Printf("Привет, я - %s, я работаю в %s. Звоните мне по номеру %s\n", e.name, + e.company, e.phone) //Да, можно разбить строку на 2 строки. +} - // Employee перегружает метод SayHi - func (e *Employee) SayHi() { - fmt.Printf("Привет, я - %s, я работаю в %s. Звоните мне по номеру %s\n", e.name, - e.company, e.phone) //Да, можно разбить строку на 2 строки. - } +func (s *Student) BorrowMoney(amount float32) { + s.loan += amount // (снова и снова...) +} - func (s *Student) BorrowMoney(amount float32) { - s.loan += amount // (снова и снова...) - } +func (e *Employee) SpendSalary(amount float32) { + e.money -= amount +} - func (e *Employee) SpendSalary(amount float32) { - e.money -= amount - } +// определяем интерфейс +type Men interface { + SayHi() + Sing(lyrics string) + Guzzle(beerStein string) +} - // определяем интерфейс - type Men interface { - SayHi() - Sing(lyrics string) - Guzzle(beerStein string) - } - - type YoungChap interface { - SayHi() - Sing(song string) - BorrowMoney(amount float32) - } - - type ElderlyGent interface { - SayHi() - Sing(song string) - SpendSalary(amount float32) - } +type YoungChap interface { + SayHi() + Sing(song string) + BorrowMoney(amount float32) +} +type ElderlyGent interface { + SayHi() + Sing(song string) + SpendSalary(amount float32) +} +``` Интерфейс может быть реализован любым типом данных, и один тип может реализовывать несколько интерфейсов одновременно. Заметьте, что все типы реализуют пустой интерфейс `interface{}`, так как у него нет методов, а все типы изначально также не имеют методов. @@ -92,95 +92,95 @@ Итак, какие типы значений может принимать интерфейс? Если мы определили переменную типа interface, то значение любого типа, который реализует этот интерфейс, может быть присвоено этой переменной. Как в примере выше, если мы определили переменную "m" как интерфейс Men, то все значения типа Student, Human или Employee могут быть присвоены переменной "m". Так что у нас может быть срез элементов типа Men, и значение любого типа, реализующего интерфейс Men, может присвоено элементам этого среза. Но имейте в виду, что срез элементов типа interface не ведет себя так же, как срез из элементов других типов. +```Go +package main - package main - - import "fmt" - - type Human struct { - name string - age int - phone string - } - - type Student struct { - Human - school string - loan float32 - } - - type Employee struct { - Human - company string - money float32 - } - - func (h Human) SayHi() { - fmt.Printf("Привет, я - %s, мой номер телефона - %s\n", h.name, h.phone) - } - - func (h Human) Sing(lyrics string) { - fmt.Println("Ля ля ля ля...", lyrics) - } - - func (e Employee) SayHi() { - fmt.Printf("Привет, я - %s, я работаю в %s. Звоните мне по номеру %s\n", e.name, - e.company, e.phone) //Да, здесь можно разбить строку на две. - } - - // Интерфейс Men реализуется типами Human, Student и Employee - type Men interface { - SayHi() - Sing(lyrics string) - } - - func main() { - mike := Student{Human{"Майк", 25, "222-222-XXX"}, "MIT", 0.00} - paul := Student{Human{"Пол", 26, "111-222-XXX"}, "Harvard", 100} - sam := Employee{Human{"Сэм", 36, "444-222-XXX"}, "Golang Inc.", 1000} - tom := Employee{Human{"Сэм", 36, "444-222-XXX"}, "Things Ltd.", 5000} - - // определяем интерфейс i - var i Men - - //i может быть Student - i = mike - fmt.Println("Это Майк, студент:") - i.SayHi() - i.Sing("November rain") - - //i может быть Employee - i = tom - fmt.Println("Это Том, работник:") - i.SayHi() - i.Sing("Born to be wild") - - // срез из элементов типа Men - fmt.Println("Давайте создадим срез из Men и посмотрим, что получится") - x := make([]Men, 3) - // Эти три элемента относятся к разным типам, но все они реализуют интерфейс Men - x[0], x[1], x[2] = paul, sam, mike - - for _, value := range x { - value.SayHi() - } - } +import "fmt" +type Human struct { + name string + age int + phone string +} + +type Student struct { + Human + school string + loan float32 +} + +type Employee struct { + Human + company string + money float32 +} + +func (h Human) SayHi() { + fmt.Printf("Привет, я - %s, мой номер телефона - %s\n", h.name, h.phone) +} + +func (h Human) Sing(lyrics string) { + fmt.Println("Ля ля ля ля...", lyrics) +} + +func (e Employee) SayHi() { + fmt.Printf("Привет, я - %s, я работаю в %s. Звоните мне по номеру %s\n", e.name, + e.company, e.phone) //Да, здесь можно разбить строку на две. +} + +// Интерфейс Men реализуется типами Human, Student и Employee +type Men interface { + SayHi() + Sing(lyrics string) +} + +func main() { + mike := Student{Human{"Майк", 25, "222-222-XXX"}, "MIT", 0.00} + paul := Student{Human{"Пол", 26, "111-222-XXX"}, "Harvard", 100} + sam := Employee{Human{"Сэм", 36, "444-222-XXX"}, "Golang Inc.", 1000} + tom := Employee{Human{"Сэм", 36, "444-222-XXX"}, "Things Ltd.", 5000} + + // определяем интерфейс i + var i Men + + //i может быть Student + i = mike + fmt.Println("Это Майк, студент:") + i.SayHi() + i.Sing("November rain") + + //i может быть Employee + i = tom + fmt.Println("Это Том, работник:") + i.SayHi() + i.Sing("Born to be wild") + + // срез из элементов типа Men + fmt.Println("Давайте создадим срез из Men и посмотрим, что получится") + x := make([]Men, 3) + // Эти три элемента относятся к разным типам, но все они реализуют интерфейс Men + x[0], x[1], x[2] = paul, sam, mike + + for _, value := range x { + value.SayHi() + } +} +``` Интерфейс - это набор абстрактных методов, он может быть реализован типами, не являющимися интерфейсами. Поэтому он не может быть реализован самим собой. ### Пустой интерфейс -Пустой интерфейс - это интерфейс, который не содержит методов. Это очен полезно, если мы хотим хранить данные любого типа в одном месте, и это похоже на void* в C. - - // Определим a как пустой интерфейс - var a interface{} - var i int = 5 - s := "Привет, мир!" - // a может принимать значение любого типа - a = i - a = s - +Пустой интерфейс - это интерфейс, который не содержит методов. Это очень полезно, если мы хотим хранить данные любого типа в одном месте, и это похоже на void* в C. +```Go +// Определим a как пустой интерфейс +var a interface{} +var i int = 5 +s := "Привет, мир!" +// a может принимать значение любого типа +a = i +a = s +``` Если функция использует пустой интерфейс в качестве входного аргумента, она может принимать значения любого типа; если функция использует пустой интерфейс в качестве возвращаемого значения, она может возвращать значения любого типа. ### Интерфейсы как аргументы методов @@ -196,37 +196,37 @@ type Stringer interface { ``` Это значит, что любой тип, реализующий интерфейс Stringer, может быть передан в качестве аргумента в fmt.Println. Давайте докажем это: +```Go +package main - package main +import ( + "fmt" + "strconv" +) - import ( - "fmt" - "strconv" - ) +type Human struct { + name string + age int + phone string +} - type Human struct { - name string - age int - phone string - } - - // Human реализует fmt.Stringer - func (h Human) String() string { - return "Имя:" + h.name + ", Возраст:" + strconv.Itoa(h.age) + " years, Контакт:" + h.phone - } - - func main() { - Bob := Human{"Боб", 39, "000-7777-XXX"} - fmt.Println("Этот человек: ", Bob) - } +// Human реализует fmt.Stringer +func (h Human) String() string { + return "Имя:" + h.name + ", Возраст:" + strconv.Itoa(h.age) + " years, Контакт:" + h.phone +} +func main() { + Bob := Human{"Боб", 39, "000-7777-XXX"} + fmt.Println("Этот человек: ", Bob) +} +``` Возвращаясь к примеру с Box можно обнаружить, что Color также реализует интерфейс Stringer, поэтому у нас есть возможность изменить формат вывода информации. Если не реализовать этот интерфейс, fmt.Println выведет тип на печать в формате по умолчанию. - - fmt.Println("Самая большая коробка: ", boxes.BiggestsColor().String()) - fmt.Println("Самая большая коробка: ", boxes.BiggestsColor()) - -Внимание: Если тип реализует интерфейс `error`, fmt вызовет `error()`, поэтому в этом случае Вам не надо реализовывать Stringer. +```Go +fmt.Println("Самая большая коробка: ", boxes.BiggestsColor().String()) +fmt.Println("Самая большая коробка: ", boxes.BiggestsColor()) +``` +Внимание: Если тип реализует интерфейс `error`, fmt вызовет `Error()`, поэтому в этом случае Вам не надо реализовывать Stringer. ### Тип переменной в интерфейсе @@ -239,90 +239,90 @@ type Stringer interface { Если element является переменной типа, который мы указали, ok будет равен true, иначе - false. Чтобы было понятнее, посмотрим на пример: +```Go +package main - package main - - import ( - "fmt" - "strconv" - ) - - type Element interface{} - type List []Element - - type Person struct { - name string - age int - } - - func (p Person) String() string { - return "(Имя: " + p.name + " - возраст: " + strconv.Itoa(p.age) + " лет)" - } - - func main() { - list := make(List, 3) - list[0] = 1 // целочисленный тип - list[1] = "Привет" // строка - list[2] = Person{"Деннис", 70} - - for index, element := range list { - if value, ok := element.(int); ok { - fmt.Printf("list[%d] - это целое число, его значение - %d\n", index, value) - } else if value, ok := element.(string); ok { - fmt.Printf("list[%d] - это строка, его значение - %s\n", index, value) - } else if value, ok := element.(Person); ok { - fmt.Printf("list[%d] - это Person, его значение %s\n", index, value) - } else { - fmt.Printf("list[%d] - это данные какого-то другого типа\n", index) - } +import ( + "fmt" + "strconv" +) + +type Element interface{} +type List []Element + +type Person struct { + name string + age int +} + +func (p Person) String() string { + return "(Имя: " + p.name + " - возраст: " + strconv.Itoa(p.age) + " лет)" +} + +func main() { + list := make(List, 3) + list[0] = 1 // целочисленный тип + list[1] = "Привет" // строка + list[2] = Person{"Деннис", 70} + + for index, element := range list { + if value, ok := element.(int); ok { + fmt.Printf("list[%d] - это целое число, его значение - %d\n", index, value) + } else if value, ok := element.(string); ok { + fmt.Printf("list[%d] - это строка, его значение - %s\n", index, value) + } else if value, ok := element.(Person); ok { + fmt.Printf("list[%d] - это Person, его значение %s\n", index, value) + } else { + fmt.Printf("list[%d] - это данные какого-то другого типа\n", index) } } - +} +``` Пользоваться этим шаблоном довольно-таки просто, но если надо протестировать много типов, лучше воспользоваться `switch`. - тест с использованием switch Давайте перепишем наш пример с использованием `switch`. - - package main - - import ( - "fmt" - "strconv" - ) - - type Element interface{} - type List []Element - - type Person struct { - name string - age int - } - - func (p Person) String() string { - return "(Имя: " + p.name + " - возраст: " + strconv.Itoa(p.age) + " лет)" - } - - func main() { - list := make(List, 3) - list[0] = 1 // целое число - list[1] = "Hello" // строка - list[2] = Person{"Деннис", 70} - - for index, element := range list { - switch value := element.(type) { - case int: - fmt.Printf("list[%d] - целое число, его значение - %d\n", index, value) - case string: - fmt.Printf("list[%d] - строка, его значение - %s\n", index, value) - case Person: - fmt.Printf("list[%d] - Person, его значение - %s\n", index, value) - default: - fmt.Println("list[%d] - данные какого-то другого типа", index) - } +```Go +package main + +import ( + "fmt" + "strconv" +) + +type Element interface{} +type List []Element + +type Person struct { + name string + age int +} + +func (p Person) String() string { + return "(Имя: " + p.name + " - возраст: " + strconv.Itoa(p.age) + " лет)" +} + +func main() { + list := make(List, 3) + list[0] = 1 // целое число + list[1] = "Hello" // строка + list[2] = Person{"Деннис", 70} + + for index, element := range list { + switch value := element.(type) { + case int: + fmt.Printf("list[%d] - целое число, его значение - %d\n", index, value) + case string: + fmt.Printf("list[%d] - строка, его значение - %s\n", index, value) + case Person: + fmt.Printf("list[%d] - Person, его значение - %s\n", index, value) + default: + fmt.Println("list[%d] - данные какого-то другого типа", index) } } - +} +``` Нужно запомнить, что конструкция `element.(type)` не может быть использована вне тела `switch`, в этом случае надо использовать шаблон `запятая-ok`. @@ -331,63 +331,63 @@ type Stringer interface { В синтаксисе Go существует множество встроенной логики, такой, например, как анонимные поля в структуре. Неудивительно, что мы можем использовать в качестве анонимных полей и интерфейсы тоже, но называются они `Встроенные интерфейсы`. В этом случае мы следуем тем же правилам, что и в случае со встроенными полями. А точнее, если в интерфейс встроен другой интерфейс, то этот интерфейс будет иметь в себе все методы встроенного интерфейса. В исходном коде пакета `container/heap` мы можем видеть следующее определение: - - type Interface interface { - sort.Interface // встраиваемый sort.Interface - Push(x interface{}) // метод Push для того, чтобы помещать элементы в кучу - Pop() interface{} // метод Pop, который изымает элементы из кучи - } - +```Go +type Interface interface { + sort.Interface // встраиваемый sort.Interface + Push(x interface{}) // метод Push для того, чтобы помещать элементы в кучу + Pop() interface{} // метод Pop, который изымает элементы из кучи +} +``` Мы видим, что `sort.Interface` является встраиваемым интерфейсом, поэтому в Interface неявно присутствуют три метода, содержащиеся внутри `sort.Interface`: - - type Interface interface { - // Len - количество элементов в коллекции - Len() int - // Less определяет, надо ли перемещать элемент с индексом i - // перед элементом с индексом j. - Less(i, j int) bool - // Swap меняем местами элементы с индексами i и j. - Swap(i, j int) - } - +```Go +type Interface interface { + // Len - количество элементов в коллекции + Len() int + // Less определяет, надо ли перемещать элемент с индексом i + // перед элементом с индексом j. + Less(i, j int) bool + // Swap меняем местами элементы с индексами i и j. + Swap(i, j int) +} +``` Другой пример - `io.ReadWriter` из пакета `io`. - - // io.ReadWriter - type ReadWriter interface { - Reader - Writer - } - +```Go +// io.ReadWriter +type ReadWriter interface { + Reader + Writer +} +``` ### Рефлексия Рефлексия в Go используется для определения информации во время выполнения программы. Мы пользумеся пакетом `reflect`, и эта официальная [статья](http://golang.org/doc/articles/laws_of_reflection.html) объясняет, как reflect работает в Go. В процессе использования reflect задействованы 3 шага. Во-первых, нужно конвертировать интерфейс в типы reflect (reflect.Type или reflect.Value в зависимости от ситуации). - - t := reflect.TypeOf(i) // получает мета-данные типа i в переменную t - v := reflect.ValueOf(i) // получает значение типа i в переменную v - +```Go +t := reflect.TypeOf(i) // получает мета-данные типа i в переменную t +v := reflect.ValueOf(i) // получает значение типа i в переменную v +``` После этого мы может конвертировать типы, полученные в результате рефлексии, для того, чтобы получить нужные нам значения. - - var x float64 = 3.4 - v := reflect.ValueOf(x) - fmt.Println("Тип:", v.Type()) - fmt.Println("Вид является float64:", v.Kind() == reflect.Float64) - fmt.Println("Значение:", v.Float()) - +```Go +var x float64 = 3.4 +v := reflect.ValueOf(x) +fmt.Println("Тип:", v.Type()) +fmt.Println("Вид является float64:", v.Kind() == reflect.Float64) +fmt.Println("Значение:", v.Float()) +``` Наконец, если мы хотим изменить значения типов, полученных в результате рефлексии, нам нужно сделать их изменяемыми. Как было обсуждено ранее, есть разница между передачей по ссылке и по значению. Следующий код не скомпилируется: - - var x float64 = 3.4 - v := reflect.ValueOf(x) - v.SetFloat(7.1) - +```Go +var x float64 = 3.4 +v := reflect.ValueOf(x) +v.SetFloat(7.1) +``` Вместо этого для изменения значений типов, полученных в результате рефлексии, нам нужно использовать следующий код: - - var x float64 = 3.4 - p := reflect.ValueOf(&x) - v := p.Elem() - v.SetFloat(7.1) - +```Go +var x float64 = 3.4 +p := reflect.ValueOf(&x) +v := p.Elem() +v.SetFloat(7.1) +``` Мы здесь обсудили основы рефлексии, однако, чтобы больше понять, Вы должны больше практиковаться. ## Ссылки