Tour of Go를 기반으로한 Go의 기본 내용입니다.
Interface
- Interface는 메소드의 시그니쳐 집합으로 정의된다.
- 인터페이스 유형의 값은 해당 인터페이스의 메소드를 모두 구현하는 타입이라면 어떤 유형이든 가질 수 있음.
package main
import (
"fmt"
"math"
)
type Abser interface {
Abs() float64
}
func main() {
var a Abser
f := MyFloat(-math.Sqrt2)
v := Vertex{3, 4}
a = f // a MyFloat implements Abser
a = &v // a *Vertex implements Abser
// In the following line, v is a Vertex (not *Vertex)
// and does NOT implement Abser.
//a = v //Vertex포인터는 Abs가 구현되어있지만 Vertex는 그렇지 않으므로 오류!
fmt.Println(a.Abs())
}
type MyFloat float64
func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
type Vertex struct {
X, Y float64
}
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
위 예제를 보게되면 Vertex struct 타입과 Myfloat 타입을 통해서 어떻게 interface구현이 되는지 확인 할 수 있다.
여기서 Myfloat는 Myfloat에 Abser인터페이스가 요구하는 Abs메서드를 구현하고 있고, Vertex는 Vertex포인터에서 Abs메서드를 구현하고 있음을 알 수 있다. 이때 주의해야할 것은 Vertex의 경우는, Vertex포인터에 Abs 메서드가 매핑이 되어있기 때문에 Vertex 타입은 Abser 인터페이스가 받을 수 없다는 점이다!
- 인터페이스의 구현은 JAVA와 같이 implement하는 것과 같이 명시적으로 이루어지지 않음.(암시적 구현)
package main
import "fmt"
type I interface {
M()
}
type T struct {
S string
}
// 이 메서드는 type T가 interface I를 구현(implement)함을 의미하지만,
// 그것을 명시하지는 않는다.
func (t T) M() {
fmt.Println(t.S)
}
func main() {
var i I = T{"hello"}
i.M()
}
- 인터페이스의 값은 값과 그 값의 타입으로 이루어짐.
- 이는 인터페이스로 메서드를 호출하면 기본 형식에 동일한 함수가 실행됨을 의미한다.
package main
import (
"fmt"
"math"
)
type I interface {
M()
}
type T struct {
S string
}
func (t *T) M() {
fmt.Println(t.S)
}
type F float64
func (f F) M() {
fmt.Println(f)
}
func main() {
var i I
i = &T{"Hello"}
describe(i)
i.M()
i = F(math.Pi)
describe(i)
i.M()
}
func describe(i I) {
fmt.Printf("(%v, %T)\n", i, i)
}
- Nil 인터페이스 구분
- 인터페이스 자체에 할당된 값이 0일 경우, 그 메서드는 nil 리시버로 호출된다. 이것이 인터페이스가 nil을 의미 하지 않음.
- 인터페이스에 아무 값도 할당되지 않았을 경우, 이 인터페이스는 nil 인터페이스이다.
package main
import "fmt"
type I interface {
M()
}
type T struct {
S string
}
func (t *T) M() {
if t == nil {
fmt.Println("<nil>")
return
}
fmt.Println(t.S)
}
func main() {
var i I
var t *T
i = t
describe(i)
i.M()
i = &T{"hello"}
describe(i)
i.M()
// 아래 예시의 경우, I인터페이스 j자체가 nil이므로 에러 발생!
//var j I
//describe(j)
//j.M()
}
func describe(i I) {
fmt.Printf("(%v, %T)\n", i, i)
}
- 빈 인터페이스는 모든 유형의 값을 가질 수 있습니다.
- 빈 인터페이스는 알 수 없는 값을 처리할 때 사용되고, 대표적인 예로 fmt의 Print입니다.
package main
import "fmt"
func main() {
var i interface{}
describe(i)
i = 42
describe(i)
i = "hello"
describe(i)
}
func describe(i interface{}) { //빈 인터페이스로 아무값이나 받을 수 있음.
fmt.Printf("(%v, %T)\n", i, i)
}
Type assertion
- type assertion은 인터페이스의 기존 값을 접근할 수 있도록 해준다.
t := i.(T)
- 위 예시는 i는 type T의 값을 가지고 있다면, 그것을 변수 t에 type T로 할당한다는 뜻.
- 만약 i가 T를 가지고 있지 않다면 panic 상태로 들어간다.
- type assertiond를 테스트하고 싶다면 type assert의 성공여부를 판단하는 변수를 받아서 확인하면 된다.
t, ok := i.(T)
- 만약 type assert가 통과 되었다면 ok는 true, t에는 type T로 값이 반환될 것이고, 만약 통과되지 않았다면 ok는 false, t에는 zero가 반환되며 별도로 panic은 발생하지 않는다.
- Type switch는 타입에 따라 특정한 로직을 실행하고 싶을때 사용된다.
switch v := i.(type) {
case T:
// v는 Type T를 가지고 있음.
case S:
// v는 Type S를 가지고 있음.
default:
// 매칭되는 값 없음.
}
- case는 v의 타입과 비교되며 일치하는 부분의 로직이 실행됨.
package main
import "fmt"
func do(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)
}
}
func main() {
do(21)
do("hello")
do(true)
var test interface{} = 2
result, ok := test.(string)
fmt.Println(result,ok)
}
/*result :
Twice 21 is 42
"hello" is 5 bytes long
I don't know about type bool!
false
*/
**Stringer, Error등 Tour of Go에서 제공하는 예제로 실제 인터페이스를 구현해보는 연습도 해보시기 바랍니다!
Stringer 문제
package main
import "fmt"
type IPAddr [4]byte
// TODO: Add a "String() string" method to IPAddr.
func (ipaddr IPAddr) String() string{
return fmt.Sprintf("%d.%d.%d.%d",ipaddr[0],ipaddr[1],ipaddr[2],ipaddr[3])
}
func main() {
hosts := map[string]IPAddr{
"loopback": {127, 0, 0, 1},
"googleDNS": {8, 8, 8, 8},
}
for name, ip := range hosts {
fmt.Printf("%v: %v\n", name, ip)
}
}
/* result :
loopback: 127.0.0.1
googleDNS: 8.8.8.8
*/
Errors 문제
package main
import (
"fmt"
)
type ErrNegativeSqrt float64
func (e ErrNegativeSqrt)Error() string {
return fmt.Sprintf("cannot Sqrt negative number: %v",float64(e))
}
func Sqrt(x float64) (float64, error) {
if x < 0 {
return x, ErrNegativeSqrt(x)
}
z := float64(1)
for i:=0 ; i < 10000; i++{
z -= (z*z -x) / 2*z
}
return z, nil
}
func main() {
fmt.Println(Sqrt(2))
fmt.Println(Sqrt(-2))
}
/* result :
1.4106685607006662 <nil>
-2 cannot Sqrt negative number: -2
*/
Readers 문제
package main
import "golang.org/x/tour/reader"
type MyReader struct{}
// TODO: Add a Read([]byte) (int, error) method to MyReader.
func (r MyReader) Read(b []byte)(int, error){
for i := range b {
b[i] = 'A'
}
return len(b), nil
}
func main() {
reader.Validate(MyReader{})
}
/* result :
OK!
*/
rot13Reader 문제
package main
import (
"io"
"os"
"strings"
"errors"
)
type rot13Reader struct {
r io.Reader
}
func (rot *rot13Reader)Read(buf []byte) (n int, err error){
n, err = rot.r.Read(buf)
for i, v := range buf {
_rot13 := v+13
if (_rot13 >= 'a' && _rot13 <= 'z') || (_rot13 >= 'A' && _rot13 <= 'Z'){
buf[i] = _rot13
}else{
buf[i] = _rot13 - 26
}
}
if err != nil {
err = errors.New("EOF")
return
}
return
}
func main() {
s := strings.NewReader("Lbh penpxrq gur pbqr!")
r := rot13Reader{s}
io.Copy(os.Stdout, &r)
}
/* result :
You cracked the code
*/
....
'Language > Go' 카테고리의 다른 글
[기초] Go 기초 정리 - 4 (Method) (0) | 2022.07.05 |
---|---|
구조체를 포함하는 구조체 (0) | 2022.07.04 |
[기초] Go 기초 정리 - 3 (Pointer, Struct, Array, Slice, Range, Map, Function value, Function Closure) (0) | 2022.06.26 |
[기초] Slice의 사용과 내부 작동 방식 (0) | 2022.06.23 |
[기초] Go 기초 정리 - 2 (For, If, Switch, Defer) (0) | 2022.06.15 |