This repository has been archived by the owner on Apr 7, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 20
/
04-types_and_interfaces.slide
331 lines (197 loc) · 10.7 KB
/
04-types_and_interfaces.slide
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
Типове и интерфейси
31.10.2018
http://fmi.golang.bg/
@fmi_golang
* Но преди това...
* Въпрос за мъфин #1
Как увеличаваме броя елементи в един масив? Можем ли да сравняваме масиви с оператора `"=="` ?
- Не може да променяме големината на масиви (само на slices)
- Да, можем да сравняваме с `==`
* Въпрос за мъфин #2
arr := [6]float64{1, 2, 3, 4, 5, 6}
x := arr[1:]
y := append(x, 4)
Какви типове имат `arr`, `x`, `y`?
Какво ще върнат `len(x)`, `cap(x)`, `len(y)`, `cap(y)`?
- [6]float64
- []float64
- []float64
- 5
- 5
- 6
- 10
* Въпрос за мъфин #3
Кои от следните типове са допустими ключове на map?
- `string`
- `[5]int`
- `[]int`
- `struct{int}`
- `*[]int`
* Въпрос за мъфин #3
Отговор:
- Всички без `[]int`
- ...да, `*[]int` допустим ключ, макар и не много полезен:
.play code/concurrency101/map-keys.go /^func main/,
* Въпрос за мъфин #4
Каква е разликата между `new()` и `make()`, кога се ползва едното и кога другото и какво връщат?
- `new` само заделя памет и я нулира, за разлика от `make`, което инициализира обекта
- `new` ползваме за наши типове (структури), а `make` за вградени типове като slices и maps
- `new` връща указател, `make` връща стойност
* Въпрос за мъфин #5
Как инициализираме thread-safe slice или map?
- Няма такова животно
- Трябва сами да се погрижим за thread-safe достъп до тях
* Типове и интерфейси
* Собствени типове
- От всеки тип в езика можем да създаваме наши типове
- Не се различават по нищо от вградените
type integer int
type float float64
type chars string
- Не особено полезно, на пръв поглед
- Но те могат и повече
* Нека разгледаме функцията Abs
func Abs(i integer) integer {
switch {
case i < 0:
return -i
case i == 0:
return 0
default:
return i
}
}
var number integer = -42
positiveInteger := Abs(number)
- Така се дефинира обикновена функция `Abs`, която се извиква като ѝ се подаде integer като аргумент
- Това не е "обектно-ориентираният" начин да се направи подобно нещо
* "Обектно-ориентираният" начин да се направи подобно нещо
func (i integer) Abs() integer {
switch {
case i < 0:
return -i
case i == 0:
return 0
default:
return i
}
}
var number integer = -42
number.Abs()
* Какво точно е метод?
- Методите се изпълняват върху конкретен тип
- Той се нарича receiver
- Методите могат да се дефинират *само* върху типове, дефинирани в същия пакет
- Като следствие от горното правило, методите имат достъп до private полетата в типа
- На практика, дефинират държанието на типа
* Що е то receiver-а?
- Няма фиксирана ключова дума за това.
- Има просто конвенция (първата/ите букви от името на типа)
- Той може да бъде по стойност, както и указател
По стойност
- Работи се върху копие на обекта
- Това може да е скъпа операция за големи обекти
Като указател
- Работи се върху копие на указателя към обект
- Всяка промяна в метода се отразява на оригиналния обект
Няма различен синтаксис за използването на двата вида receiver-и.
* Пример
.play code/types_and_interfaces/integer.go /^type/,/end/
* nil обекти
Извикването на методи работи дори с nil pointers към обекти:
.play code/types_and_interfaces/nil_objects.go /^type/,/end/
Внимавайте с тях :)
* struct
- Същите неща могат да се правят и върху композитни типове
- Полета и методи с малка буква са private
- Нека си дефинираме типове за триъгълник и правоъгълник
.code code/types_and_interfaces/shapes.go /start types/,/end types/
* Методи за тези типове
.code code/types_and_interfaces/shapes.go /start methods/,/end methods/
* "Завързани" методи
.play code/types_and_interfaces/bound_methods.go /^type/,
* Интерфейси
- Какво правим, ако искаме да имаме списък от геометрични фигури, на които ще търсим обиколка или лице?
- Тези типове нямат общ "родител"
- Няма нужда и да са в един пакет
- Това в Go се постига с интерфейси
- Интерфейсите са както съвкупност от методи, така и тип от езика
- Общият интерфейс на двете фигури са методите `Circumference()` и `Area()`
- Нашите фигури нямат представа, че такъв интерфейс съществува
* Stringer
type Stringer interface {
String() string
}
Това е интерфейс от стандартната библиотека, дефиниран в пакета `fmt`.
Ако имплементираме този интефейс за наш тип, може да променим начина, по който `fmt.Printf()` го принтира чрез `%s`.
.play code/types_and_interfaces/stringer.go /start/,/end/
* Структура
type Binary uint64
.image assets/interface0.png
.image assets/interface.png
По-подробно обяснение може да намерите тук: [[http://research.swtch.com/interfaces]]
* Дефиниция на интерфейс
.code code/types_and_interfaces/shapes.go /start interface/,/end interface/
- Това е нов абстрактен тип, от който не можем да създаваме обекти, но можем да имаме променливи от него
- Всеки тип, който има методите `Circumference` и `Area` със същата сигнатура, имплементира `Shape`
- Двата типа `Triangle` и `Rectangle` *имплицитно* го имплементират
- Променливите от тип интерфейс са първокласни обекти в Go
* Пример
.play code/types_and_interfaces/shapes.go /start funcs/,/end funcs/
* Вложени типове
- Можем да накараме един тип да присвои държанието на друг тип
- Това не е наследяване в смисъла на OOP
- Влагащият тип не е от тип вложения (демек няма is-a релация)
- Просто получава всички негови полета и методи
- Има два начина да ги използваме
* Композиция
_Конструираме_един_тип,_комбинирайки_няколко_прости_други_типa._
*** Пример:
Искаме да си направим smartphone. Не откриваме топлата вода, а просто го наблъскваме с каквито джаджи се сетим.
type Smartphone struct {
phone BasicPhone
camera CameraModule
wifi WiFiModule
screen MultiTouchScreen
battery DamnHugeBattery
gyroscope SmallGyroscope
gps GPSModule
secret CanOpener
}
Всеки един от тези типове отговаря за точно едно нещо и може да бъде използвано самостоятелно.
* Квази-Наследяване
Вярваме, че знаете как работи то. Дори сме сигурни, че сте правили хора и студенти:
.play code/types_and_interfaces/anon_structs.go /start/,/end/
Вложеният тип Person е анонимен, което присвоява всичките му методи и атрибути на базовия тип.
* Множествено "наследяване"
Да, имате право на много анонимни вложени типа.
Не, това не е яко.
Да, не очакваме да го ползвате често.
* Duck typing
Всеки обект имплементира празния интерфейс:
interface{}
Съответно на променлива от такъв тип може да присвоим всякаква стойност!
Но с нея не можем да правим абсолютно нищо :)
Това може да звучи безполезно, но не е, тъй като имаме...
* Type Assertion
.play code/types_and_interfaces/type_assertion.go
В `str` ще имаме стойността на `value`, ако тя наистина е била от тип `string`.
* Type Switch
.play code/types_and_interfaces/interface_conversions.go /^type mystruct/,
Така може да правим различни неща въз основа на типа на променлива.
* JSON
.play code/types_and_interfaces/plain_json.go /^type Rectangle/,
* JSON
type Marshaler interface {
MarshalJSON() ([]byte, error)
}
type Unmarshaler interface {
UnmarshalJSON([]byte) error
}
* JSON
.play code/types_and_interfaces/custom_json.go /start/,/end/
* Type Aliases
type integerAlias = int
*Не* създава нов тип, a псевдоним на вече съществуващ тип
.play -edit code/types_and_interfaces/type_aliases.go /START OMIT/,/END OMIT/