-
Notifications
You must be signed in to change notification settings - Fork 0
/
4_methods_inferfaces.go
331 lines (282 loc) · 6.85 KB
/
4_methods_inferfaces.go
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
// methods and interfaces
package main
import (
"fmt"
"image"
"io" // read data
"math"
"strings"
"time"
)
// non-struct type declaration (can only have methods of types within the same package)
type MyFloat float64
// methods have a special receiver argument
// func <receiver> <functionName>() <returnType> {}
func methods() {
basicsMethods()
pointerReceivers()
}
// vertex with (x,y) coordinates
type Vertex struct {
X, Y float64
}
// calling methods
func basicsMethods() {
// create vertex and compute value
v := Vertex{3, 4}
fmt.Println(v.Abs())
// absolute value of non-struct type
f := MyFloat(-math.Sqrt2) // convert float to MyFloat
fmt.Println(f.Abs())
}
// get Vertex square root of (X^2 + Y^2)
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
// get the absolute value of f
func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
// better performance (avoid copying values)
// methods can receive pointers (not for pointers to pointers)
// can modify the value to which the receiver points
// can send value or pointer to method (automatically converts)
func pointerReceivers() {
// send Vertex value
v := Vertex{3, 4}
v.Scale(10)
fmt.Println(v.Abs())
// send pointer to Vertex
p := &Vertex{4, 3}
p.Scale(3)
fmt.Println(p.Abs())
}
// receives pointer and modifies values to multiply with f
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
// set of method signatures
// type <interfaceName>er interface {}
func interfaces() {
basicsInterfaces()
interfaceValues()
types()
keyExamples()
readers()
images()
}
// all values of type float64 automatically implements Abs()
type Abser interface {
Abs() float64
}
// creating of interfaces
func basicsInterfaces() {
var a Abser
f := MyFloat(-math.Sqrt2)
v := Vertex{3, 4}
a = f // a MyFloat implements Abser
fmt.Println(a.Abs())
a = &v // a *Vertex implements Abser
fmt.Println(a.Abs())
// TODO: why does the below run? Find out why it shouldn't.
a = v // a Vertex does NOT implement Abser
fmt.Println(a.Abs())
// implicit interface implementation
var i I = X{"hello"}
i.M()
}
// all types can implement M()
type I interface {
M()
}
// struct with type string S
type X struct {
S string
}
// struct with type string S
type T struct {
S string
}
// X implements interface I and thus implements M() (don't need to declare explicitly)
func (x X) M() {
fmt.Println(x.S)
}
// T implements interface I and thus implements M()
func (t *T) M() {
// should cater for underlying nil values
if t == nil {
fmt.Println("<nil>")
return
}
fmt.Println(t.S)
}
// MyFloat implements interface I and thus implements M()
func (f MyFloat) M() {
fmt.Println(f)
}
// interface values tuple (<value>, <type>)
func interfaceValues() {
// Calling a method on an interface value executes the method of the same name on its underlying type.
var i I
i = &T{"Hello"}
describe(i)
i.M()
i = MyFloat(math.Pi)
describe(i)
i.M()
// nil underlying values (note nil interface value causes runtime exception)
var t *T
i = t
describe(i)
i.M() // outputs <nil> due to explicit if statement catering for it
// empty interface holds any value type (for unknown types)
var emptyInterface interface{}
describeUnknown(emptyInterface)
emptyInterface = 42
describeUnknown(emptyInterface)
emptyInterface = "hello"
describeUnknown(emptyInterface)
}
// interface type assertions and switches
// type assertion: access interface's underlying concrete value
// type switch: permits several type assertions in series
func types() {
// type assertion
var i interface{} = "hello"
s := i.(string) // assert that i holds the concrete type string
fmt.Println(s)
// test if interface holds a specific type
s, ok := i.(string) // (<underlyingValue>, <boolean>)
fmt.Println(s, ok)
f, ok := i.(float64)
fmt.Println(f, ok)
// f = i.(float64) // error since i does not hold a float64
// fmt.Println(f)
// type switches
typeSwitch(21)
typeSwitch("hello")
typeSwitch(true)
}
// type switch - do different things depending on the type (unsure which type hence the empty interface parameter)
func typeSwitch(i interface{}) {
switch v := i.(type) {
case int:
fmt.Printf("Twice %v is %v\n", v, v*2)
case string:
fmt.Printf("%q is %v bytes long\n", v, len(v))
default:
fmt.Printf("I don't know about type %T!\n", v)
}
}
// stringer and error interfaces
func keyExamples() {
stringers()
errors()
}
// ubiquotous interface Stringer defined by fmt
// Stringer: type that can describe itself as a string
func stringers() {
h := Person{"Harry Potter", 22}
v := Person{"Tom Riddle", 90}
fmt.Println(h, v)
}
// person with name and age
type Person struct {
Name string
Age int
}
// implements interface Stringer (defined by fmt)
func (p Person) String() string {
return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}
// ubiquotous interface Error defined by fmt
// express error state
func errors() {
// if the value returned by run is not nil, then print the value
if val := run(); val != nil {
fmt.Println(val)
}
// sqrt complex number error check
fmt.Println(Sqrt(2))
fmt.Println(Sqrt(-2))
}
// error with when and what
type MyError struct {
When time.Time
What string
}
// output formatted error
func (e *MyError) Error() string {
return fmt.Sprintf("at %v, %s", e.When, e.What)
}
// running function returns a MyError struct
func run() error {
return &MyError{
time.Now(),
"it didn't work",
}
}
// sqrt error type
type ErrNegativeSqrt float64
// sqrt error string output
func (e ErrNegativeSqrt) Error() string {
return fmt.Sprintf("cannot Sqrt negative number: %v", float64(e))
}
// Newton's method: compute sqrt using loop through guesses with error case
func Sqrt(x float64) (float64, error) {
if x < 0 {
return 0, ErrNegativeSqrt(x)
}
z := x / 2
z_prev := -z
for i := 0; i < 10; i++ {
z -= (z*z - x) / (2 * z)
// break if value not changing
if z == z_prev {
fmt.Println("Exiting loop")
break
}
z_prev = z
fmt.Println(z)
}
fmt.Printf("The sqrt of %g is ~%g\n", x, z)
return z, nil
}
// io.Reader interface (read stream of data)
func readers() {
r := strings.NewReader("Hello, Reader")
b := make([]byte, 8)
for {
// Read returns the number of bytes populated and error value (nil if no err)
n, err := r.Read(b)
fmt.Printf("n = %v err = %v b = %v\n", n, err, b)
// output value of bytes read
fmt.Printf("b[:n] = %q\n", b[:n])
// EOF error when the stream ends
if err == io.EOF {
break
}
}
}
func images() {
m := image.NewRGBA(image.Rect(0, 0, 100, 100))
fmt.Println(m.Bounds())
fmt.Println(m.At(0, 0).RGBA())
}
func main() {
fmt.Println("Running methods & interfaces")
methods()
interfaces()
}
// output value and type for values of type I
func describe(i I) {
fmt.Printf("(%v, %T)\n", i, i)
}
// output value and type for values of unknown type
func describeUnknown(i interface{}) {
fmt.Printf("(%v, %T)\n", i, i)
}