Day 6 with Go-Lang

2 minute read

Go Interface

Struct 가 필드의 집합이라면, Interface 는 메소드의 집합이다. Interface 는 Type 이 구현해야 하는 메소드 Prototype 들을 정의한다. 하나의 사용자 정의 타입이 Interface 를 구현하기 위해서는 단순히 그 인터페이스가 갖는 모든 메소드를 구현하면 된다.

인터페이스는 Struct 와 마찬가지로 Type 문을 사용해 정의한다.

type Shape interface {
	area() float64
	perimeter() float64
}
  • Interface 구현

인터페이스를 구현하기 위해서 해당 타입이 그 인터페이스의 메소드를 모두 구현하면 되므로, 위의 Shape Interface 를 구현하기 위해서는 area(), perimeter() 2 개의 메소드만 구현하면 된다.

type Rect struct {
	width, height float64
}

type Circle struct {
	radius float64
}

func (r Rect) area() float64 {
	return r.width * r.height
}

func (r Rect) perimeter() float64 {
	return 2 * (r.width + r.height)
}

func (c Circle) area() float64 {
	return math.Pi * c.radius * c.radius
}

func (c Circle) perimeter() float64 {
	return 2 * math.Pi * c.radius
}
  • Interface 사용

인터페이스를 사용하는 일반적 예로 함수가 파라미터로 인터페이스를 받아들이는 경우를 들 수 있다. 함수 Parameter 가 interface 인 경우, 이는 어떤 타입이든 해당 인터페이스를 구현하기만 하면 모두 입력 파라미터로 사용될 수 있다는 것을 의미한다.

func main() {
	r := Rect{10., 20.}
	c := Circle{10}

	showArea(r, c)
}

func showArea(shapes ...Shape) {
	for _, s := range shapes {
		a := s.area()
		println(a)
	}
}
  • Interface 타입

Go 언어에서 Empty Interface 를 자주 접하게 되는데, 흔히 Interface Type 으로 불리운다. 빈 Interface 는 interface{} 와 같이 표현한다.

func Marshal(v interface{}) ([]byte, error);
func Println(a ...interface{}) (n int, err error);

Empty Interface 는 메소드를 전혀 갖지 않는 빈 인터페이스로, Go 의 모든 Type 은 적어도 0개의 메소드를 구현하므로, 흔히 Go 에서 모든 Type 을 나타내기 위해 빈 Interface 를 사용한다.

즉, 빈 Interfae 는 어떠한 타입도 담을 수 있는 컨테이너라고 볼 수 있으며, 다른 언어에서 흔히 일컫는 Dynamic Type 이라 볼 수 있다.

package main

import "fmt"

func main() {
	var x interface{}

	x = 1
	x = "Tom"

	printIt(x)
}

func printIt(v interface{}) {
	fmt.Println(v)
}
  • Type Assertion

Interface Type 의 x 와 Type T 에 대해 x.(T) 로 표현했을 때, 이는 x 가 nil 이 아니며 x 는 T Type 에 속한다는 점을 확인하는 것으로, 이러한 표현을 Type Assertion 이라 부른다. 만약 x 가 nil 이거나 x 의 타입이 T 가 아니라면, Runtime Error 가 발생할 것이고, x 가 T 타입인 경우 T 타입의 x 를 Return 한다.

package main

func main() {
	var a interface{} = 1

	i := a
	j := a.(int)

	println(i, j)
}

Go error

Go 는 내장 타입으로 error 라는 interface 타입을 갖는다. Go 에러는 이 error interface 를 통해 주고받는데, 이 interface 는 다음과 같은 하나의 메소드를 갖는다. 개발자는 이 인터페이스를 구현하는 Custom Error Type 을 만들 수 있다.

type error interface {
	Error() string
}
  • Go Error Handling

Go 함수가 결과와 에러를 함께 리턴한다면, 이 에러가 nil 인지를 체크해 에러가 없는지를 확인할 수 있다. 예를 들어, os.Open() 함수는 func Open(name string)(file *File, err error) 와 같은 함수 원형을 갖는다. 이 경우 두 번째 Error 를 체크해서 nil 이면 에러가 없는 것이고, nil 이 아니면 err.Error() 로 해당 에러를 알 수 있다.

package main

import (
	"log"
	"os"
)

func main() {
	f, err := os.Open("C:\\temp\\1.txt")

	if err != nil {
		log.Fatal(err.Error()) // log.Fatal() 은 메시지를 출력하고 os.Exit(1) 을 호출해 프로그램을 종료한다.
	}
	println(f.Name())
}

또 다른 에러 처리로서, Error 의 Type 을 체크해서 에러 타입별로 별도의 에러 처리를 하는 방식이 있다. 아래 예제에서 otherFunc() 를 호출한 후 error 가 err 로 리턴되었을 때, 이 err 의 Type 별로 다른 처리를 하는 것을 볼 수 있다. default 의 경우, 에러타입이 nil 인 경우로서 에러가 없는 경우이고, 에러가 있으면 다음 case 문에서 에러타입이 MyError 인지를 체크하고, 아니면 다음 case 에서 일반 에러 케이스를 처리한다. 모든 에러는 error 인터페이스를 구현하므로 마지막 case 문은 모든 에러에 적용된다.

_, err := otherFunc()

switch err.(type) {
default:
	println("ok")
case MyError:
	log.Print("Log my error")
case error:
	log.Fatal(err.Error())
}

Reference : 예제로 배우는 Go 프로그래밍