Merge pull request #1075 from drypa/patch-2

format code snippets like Go code & typos fixes
This commit is contained in:
astaxie
2019-05-20 22:14:02 +08:00
committed by GitHub

View File

@@ -19,70 +19,70 @@
### Тип "Interface" ### Тип "Interface"
Интерфейс определяет набор методов, поэтому, если тип реализует эти методы, говорится, что он реализует интерфейс. Интерфейс определяет набор методов, поэтому, если тип реализует эти методы, говорится, что он реализует интерфейс.
```Go
type Human struct {
name string
age int
phone string
}
type Human struct { type Student struct {
name string Human
age int school string
phone string loan float32
} }
type Student struct { type Employee struct {
Human Human
school string company string
loan float32 money float32
} }
type Employee struct { func (h *Human) SayHi() {
Human fmt.Printf("Привет, я - %s, мой номер телефона - %s\n", h.name, h.phone)
company string }
money float32
}
func (h *Human) SayHi() { func (h *Human) Sing(lyrics string) {
fmt.Printf("Привет, я - %s, мой номер телефона - %s\n", h.name, h.phone) fmt.Println("Ля ля, ля ля ля, ля ля ля ля ля...", lyrics)
} }
func (h *Human) Sing(lyrics string) { func (h *Human) Guzzle(beerStein string) {
fmt.Println("Ля ля, ля ля ля, ля ля ля ля ля...", lyrics) fmt.Println("Guzzle Guzzle Guzzle...", beerStein)
} }
func (h *Human) Guzzle(beerStein string) { // Employee перегружает метод SayHi
fmt.Println("Guzzle Guzzle Guzzle...", beerStein) func (e *Employee) SayHi() {
} fmt.Printf("Привет, я - %s, я работаю в %s. Звоните мне по номеру %s\n", e.name,
e.company, e.phone) //Да, можно разбить строку на 2 строки.
}
// Employee перегружает метод SayHi func (s *Student) BorrowMoney(amount float32) {
func (e *Employee) SayHi() { s.loan += amount // (снова и снова...)
fmt.Printf("Привет, я - %s, я работаю в %s. Звоните мне по номеру %s\n", e.name, }
e.company, e.phone) //Да, можно разбить строку на 2 строки.
}
func (s *Student) BorrowMoney(amount float32) { func (e *Employee) SpendSalary(amount float32) {
s.loan += amount // (снова и снова...) e.money -= amount
} }
func (e *Employee) SpendSalary(amount float32) { // определяем интерфейс
e.money -= amount type Men interface {
} SayHi()
Sing(lyrics string)
Guzzle(beerStein string)
}
// определяем интерфейс type YoungChap interface {
type Men interface { SayHi()
SayHi() Sing(song string)
Sing(lyrics string) BorrowMoney(amount float32)
Guzzle(beerStein string) }
}
type YoungChap interface {
SayHi()
Sing(song string)
BorrowMoney(amount float32)
}
type ElderlyGent interface {
SayHi()
Sing(song string)
SpendSalary(amount float32)
}
type ElderlyGent interface {
SayHi()
Sing(song string)
SpendSalary(amount float32)
}
```
Интерфейс может быть реализован любым типом данных, и один тип может реализовывать несколько интерфейсов одновременно. Интерфейс может быть реализован любым типом данных, и один тип может реализовывать несколько интерфейсов одновременно.
Заметьте, что все типы реализуют пустой интерфейс `interface{}`, так как у него нет методов, а все типы изначально также не имеют методов. Заметьте, что все типы реализуют пустой интерфейс `interface{}`, так как у него нет методов, а все типы изначально также не имеют методов.
@@ -92,95 +92,95 @@
Итак, какие типы значений может принимать интерфейс? Если мы определили переменную типа interface, то значение любого типа, который реализует этот интерфейс, может быть присвоено этой переменной. Итак, какие типы значений может принимать интерфейс? Если мы определили переменную типа interface, то значение любого типа, который реализует этот интерфейс, может быть присвоено этой переменной.
Как в примере выше, если мы определили переменную "m" как интерфейс Men, то все значения типа Student, Human или Employee могут быть присвоены переменной "m". Так что у нас может быть срез элементов типа Men, и значение любого типа, реализующего интерфейс Men, может присвоено элементам этого среза. Но имейте в виду, что срез элементов типа interface не ведет себя так же, как срез из элементов других типов. Как в примере выше, если мы определили переменную "m" как интерфейс Men, то все значения типа Student, Human или Employee могут быть присвоены переменной "m". Так что у нас может быть срез элементов типа Men, и значение любого типа, реализующего интерфейс Men, может присвоено элементам этого среза. Но имейте в виду, что срез элементов типа interface не ведет себя так же, как срез из элементов других типов.
```Go
package main
package main import "fmt"
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()
}
}
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. Пустой интерфейс - это интерфейс, который не содержит методов. Это очень полезно, если мы хотим хранить данные любого типа в одном месте, и это похоже на void* в C.
```Go
// Определим a как пустой интерфейс // Определим a как пустой интерфейс
var a interface{} var a interface{}
var i int = 5 var i int = 5
s := "Привет, мир!" s := "Привет, мир!"
// a может принимать значение любого типа // a может принимать значение любого типа
a = i a = i
a = s a = s
```
Если функция использует пустой интерфейс в качестве входного аргумента, она может принимать значения любого типа; если функция использует пустой интерфейс в качестве возвращаемого значения, она может возвращать значения любого типа. Если функция использует пустой интерфейс в качестве входного аргумента, она может принимать значения любого типа; если функция использует пустой интерфейс в качестве возвращаемого значения, она может возвращать значения любого типа.
### Интерфейсы как аргументы методов ### Интерфейсы как аргументы методов
@@ -196,37 +196,37 @@ type Stringer interface {
``` ```
Это значит, что любой тип, реализующий интерфейс Stringer, может быть передан в качестве аргумента в fmt.Println. Давайте докажем это: Это значит, что любой тип, реализующий интерфейс Stringer, может быть передан в качестве аргумента в fmt.Println. Давайте докажем это:
```Go
package main
package main import (
"fmt"
"strconv"
)
import ( type Human struct {
"fmt" name string
"strconv" age int
) phone string
}
type Human struct { // Human реализует fmt.Stringer
name string func (h Human) String() string {
age int return "Имя:" + h.name + ", Возраст:" + strconv.Itoa(h.age) + " years, Контакт:" + h.phone
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)
}
func main() {
Bob := Human{"Боб", 39, "000-7777-XXX"}
fmt.Println("Этот человек: ", Bob)
}
```
Возвращаясь к примеру с Box можно обнаружить, что Color также реализует интерфейс Stringer, поэтому у нас есть возможность изменить формат вывода информации. Если не реализовать этот интерфейс, fmt.Println выведет тип на печать в формате по умолчанию. Возвращаясь к примеру с Box можно обнаружить, что Color также реализует интерфейс Stringer, поэтому у нас есть возможность изменить формат вывода информации. Если не реализовать этот интерфейс, fmt.Println выведет тип на печать в формате по умолчанию.
```Go
fmt.Println("Самая большая коробка: ", boxes.BiggestsColor().String()) fmt.Println("Самая большая коробка: ", boxes.BiggestsColor().String())
fmt.Println("Самая большая коробка: ", boxes.BiggestsColor()) fmt.Println("Самая большая коробка: ", boxes.BiggestsColor())
```
Внимание: Если тип реализует интерфейс `error`, fmt вызовет `error()`, поэтому в этом случае Вам не надо реализовывать Stringer. Внимание: Если тип реализует интерфейс `error`, fmt вызовет `Error()`, поэтому в этом случае Вам не надо реализовывать Stringer.
### Тип переменной в интерфейсе ### Тип переменной в интерфейсе
@@ -239,90 +239,90 @@ type Stringer interface {
Если element является переменной типа, который мы указали, ok будет равен true, иначе - false. Если element является переменной типа, который мы указали, ok будет равен true, иначе - false.
Чтобы было понятнее, посмотрим на пример: Чтобы было понятнее, посмотрим на пример:
```Go
package main
package main import (
"fmt"
import ( "strconv"
"fmt" )
"strconv"
) type Element interface{}
type List []Element
type Element interface{}
type List []Element type Person struct {
name string
type Person struct { age int
name string }
age int
} func (p Person) String() string {
return "(Имя: " + p.name + " - возраст: " + strconv.Itoa(p.age) + " лет)"
func (p Person) String() string { }
return "(Имя: " + p.name + " - возраст: " + strconv.Itoa(p.age) + " лет)"
} func main() {
list := make(List, 3)
func main() { list[0] = 1 // целочисленный тип
list := make(List, 3) list[1] = "Привет" // строка
list[0] = 1 // целочисленный тип list[2] = Person{"Деннис", 70}
list[1] = "Привет" // строка
list[2] = Person{"Деннис", 70} for index, element := range list {
if value, ok := element.(int); ok {
for index, element := range list { fmt.Printf("list[%d] - это целое число, его значение - %d\n", index, value)
if value, ok := element.(int); ok { } else if value, ok := element.(string); ok {
fmt.Printf("list[%d] - это целое число, его значение - %d\n", index, value) fmt.Printf("list[%d] - это строка, его значение - %s\n", index, value)
} else if value, ok := element.(string); ok { } else if value, ok := element.(Person); ok {
fmt.Printf("list[%d] - это строка, его значение - %s\n", index, value) fmt.Printf("list[%d] - это Person, его значение %s\n", index, value)
} else if value, ok := element.(Person); ok { } else {
fmt.Printf("list[%d] - это Person, его значение %s\n", index, value) fmt.Printf("list[%d] - это данные какого-то другого типа\n", index)
} else {
fmt.Printf("list[%d] - это данные какого-то другого типа\n", index)
}
} }
} }
}
```
Пользоваться этим шаблоном довольно-таки просто, но если надо протестировать много типов, лучше воспользоваться `switch`. Пользоваться этим шаблоном довольно-таки просто, но если надо протестировать много типов, лучше воспользоваться `switch`.
- тест с использованием switch - тест с использованием switch
Давайте перепишем наш пример с использованием `switch`. Давайте перепишем наш пример с использованием `switch`.
```Go
package main package main
import ( import (
"fmt" "fmt"
"strconv" "strconv"
) )
type Element interface{} type Element interface{}
type List []Element type List []Element
type Person struct { type Person struct {
name string name string
age int age int
} }
func (p Person) String() string { func (p Person) String() string {
return "(Имя: " + p.name + " - возраст: " + strconv.Itoa(p.age) + " лет)" return "(Имя: " + p.name + " - возраст: " + strconv.Itoa(p.age) + " лет)"
} }
func main() { func main() {
list := make(List, 3) list := make(List, 3)
list[0] = 1 // целое число list[0] = 1 // целое число
list[1] = "Hello" // строка list[1] = "Hello" // строка
list[2] = Person{"Деннис", 70} list[2] = Person{"Деннис", 70}
for index, element := range list { for index, element := range list {
switch value := element.(type) { switch value := element.(type) {
case int: case int:
fmt.Printf("list[%d] - целое число, его значение - %d\n", index, value) fmt.Printf("list[%d] - целое число, его значение - %d\n", index, value)
case string: case string:
fmt.Printf("list[%d] - строка, его значение - %s\n", index, value) fmt.Printf("list[%d] - строка, его значение - %s\n", index, value)
case Person: case Person:
fmt.Printf("list[%d] - Person, его значение - %s\n", index, value) fmt.Printf("list[%d] - Person, его значение - %s\n", index, value)
default: default:
fmt.Println("list[%d] - данные какого-то другого типа", index) fmt.Println("list[%d] - данные какого-то другого типа", index)
}
} }
} }
}
```
Нужно запомнить, что конструкция `element.(type)` не может быть использована вне тела `switch`, в этом случае надо использовать шаблон `запятая-ok`. Нужно запомнить, что конструкция `element.(type)` не может быть использована вне тела `switch`, в этом случае надо использовать шаблон `запятая-ok`.
@@ -331,63 +331,63 @@ type Stringer interface {
В синтаксисе Go существует множество встроенной логики, такой, например, как анонимные поля в структуре. Неудивительно, что мы можем использовать в качестве анонимных полей и интерфейсы тоже, но называются они `Встроенные интерфейсы`. В этом случае мы следуем тем же правилам, что и в случае со встроенными полями. А точнее, если в интерфейс встроен другой интерфейс, то этот интерфейс будет иметь в себе все методы встроенного интерфейса. В синтаксисе Go существует множество встроенной логики, такой, например, как анонимные поля в структуре. Неудивительно, что мы можем использовать в качестве анонимных полей и интерфейсы тоже, но называются они `Встроенные интерфейсы`. В этом случае мы следуем тем же правилам, что и в случае со встроенными полями. А точнее, если в интерфейс встроен другой интерфейс, то этот интерфейс будет иметь в себе все методы встроенного интерфейса.
В исходном коде пакета `container/heap` мы можем видеть следующее определение: В исходном коде пакета `container/heap` мы можем видеть следующее определение:
```Go
type Interface interface { type Interface interface {
sort.Interface // встраиваемый sort.Interface sort.Interface // встраиваемый sort.Interface
Push(x interface{}) // метод Push для того, чтобы помещать элементы в кучу Push(x interface{}) // метод Push для того, чтобы помещать элементы в кучу
Pop() interface{} // метод Pop, который изымает элементы из кучи Pop() interface{} // метод Pop, который изымает элементы из кучи
} }
```
Мы видим, что `sort.Interface` является встраиваемым интерфейсом, поэтому в Interface неявно присутствуют три метода, содержащиеся внутри `sort.Interface`: Мы видим, что `sort.Interface` является встраиваемым интерфейсом, поэтому в Interface неявно присутствуют три метода, содержащиеся внутри `sort.Interface`:
```Go
type Interface interface { type Interface interface {
// Len - количество элементов в коллекции // Len - количество элементов в коллекции
Len() int Len() int
// Less определяет, надо ли перемещать элемент с индексом i // Less определяет, надо ли перемещать элемент с индексом i
// перед элементом с индексом j. // перед элементом с индексом j.
Less(i, j int) bool Less(i, j int) bool
// Swap меняем местами элементы с индексами i и j. // Swap меняем местами элементы с индексами i и j.
Swap(i, j int) Swap(i, j int)
} }
```
Другой пример - `io.ReadWriter` из пакета `io`. Другой пример - `io.ReadWriter` из пакета `io`.
```Go
// io.ReadWriter // io.ReadWriter
type ReadWriter interface { type ReadWriter interface {
Reader Reader
Writer Writer
} }
```
### Рефлексия ### Рефлексия
Рефлексия в Go используется для определения информации во время выполнения программы. Мы пользумеся пакетом `reflect`, и эта официальная [статья](http://golang.org/doc/articles/laws_of_reflection.html) объясняет, как reflect работает в Go. Рефлексия в Go используется для определения информации во время выполнения программы. Мы пользумеся пакетом `reflect`, и эта официальная [статья](http://golang.org/doc/articles/laws_of_reflection.html) объясняет, как reflect работает в Go.
В процессе использования reflect задействованы 3 шага. Во-первых, нужно конвертировать интерфейс в типы reflect (reflect.Type или reflect.Value в зависимости от ситуации). В процессе использования reflect задействованы 3 шага. Во-первых, нужно конвертировать интерфейс в типы reflect (reflect.Type или reflect.Value в зависимости от ситуации).
```Go
t := reflect.TypeOf(i) // получает мета-данные типа i в переменную t t := reflect.TypeOf(i) // получает мета-данные типа i в переменную t
v := reflect.ValueOf(i) // получает значение типа i в переменную v v := reflect.ValueOf(i) // получает значение типа i в переменную v
```
После этого мы может конвертировать типы, полученные в результате рефлексии, для того, чтобы получить нужные нам значения. После этого мы может конвертировать типы, полученные в результате рефлексии, для того, чтобы получить нужные нам значения.
```Go
var x float64 = 3.4 var x float64 = 3.4
v := reflect.ValueOf(x) v := reflect.ValueOf(x)
fmt.Println("Тип:", v.Type()) fmt.Println("Тип:", v.Type())
fmt.Println("Вид является float64:", v.Kind() == reflect.Float64) fmt.Println("Вид является float64:", v.Kind() == reflect.Float64)
fmt.Println("Значение:", v.Float()) fmt.Println("Значение:", v.Float())
```
Наконец, если мы хотим изменить значения типов, полученных в результате рефлексии, нам нужно сделать их изменяемыми. Как было обсуждено ранее, есть разница между передачей по ссылке и по значению. Следующий код не скомпилируется: Наконец, если мы хотим изменить значения типов, полученных в результате рефлексии, нам нужно сделать их изменяемыми. Как было обсуждено ранее, есть разница между передачей по ссылке и по значению. Следующий код не скомпилируется:
```Go
var x float64 = 3.4 var x float64 = 3.4
v := reflect.ValueOf(x) v := reflect.ValueOf(x)
v.SetFloat(7.1) v.SetFloat(7.1)
```
Вместо этого для изменения значений типов, полученных в результате рефлексии, нам нужно использовать следующий код: Вместо этого для изменения значений типов, полученных в результате рефлексии, нам нужно использовать следующий код:
```Go
var x float64 = 3.4 var x float64 = 3.4
p := reflect.ValueOf(&x) p := reflect.ValueOf(&x)
v := p.Elem() v := p.Elem()
v.SetFloat(7.1) v.SetFloat(7.1)
```
Мы здесь обсудили основы рефлексии, однако, чтобы больше понять, Вы должны больше практиковаться. Мы здесь обсудили основы рефлексии, однако, чтобы больше понять, Вы должны больше практиковаться.
## Ссылки ## Ссылки