일반적으로 패키지는 라이브러리로서 사용되지만, "main" 이라고 명명된 패키지는 Go Compiler에 의해 특별하게 인식된다.
패키지명이 main인 경우 컴파일러는 해당 패키지를 공유 라이브러리가 아닌 실행 프로그램으로 만든다.
패키지를 공유 라이브러리로 만들 때에는, main 패키지나 main 함수를 사용하면 안된다.
패키지를 import 할 때, go 컴파일러는 GOROOT 혹은 GOPATH 환경변수를 검색하는데, 표준 패키지는 GOROOT 안의 패키지에서, 그리고 사용자 패키지나 써드파티 패키지는 GOPATH/pkg 에서 패키지를 찾게 된다.
패키지 이름의 첫문자를 대문자로 시작하면 이는 public 으로 사용할 수 있고, 소문자로 시작하게 되면 non-public 으로 패키지 내부에서만 사용될 수 있다.
init 함수 란 패키지 실행 시 처음으로 호출되는 함수이다.
패키지를 import 하고 그 패키지 안의 init() 함수만을 호출하고자 한다면, import 시 _ 라는 alias를 지정하면 된다.
Go 에서 Struct는 필드 데이터만을 가지며, 메서드를 갖지 않는다.
struct를 정의하기 위해서는 type문을 사용한다.
Go 패키지와 유사하게 Struct명의 첫문자를 대문자로 변경하면 struct를 외부에서 사용할 수 있게 된다.
type Person struct {
name string
age int
}
func main() {
p := Person{}
p.name = "John"
p.age = 20
fmt.Println(p)
}
struct 타입으로부터 객체를 생성하는 방법은 몇 가지 방법이 있다.
위의 예제처럼 먼저 빈 객체를 할당하고, 값을 채워넣는 방법이 있다.
또한 struct 필드에 접근하기 위해서 .(dot)을 사용한다.
struct 객체를 생성할 때, 초기값을 함께 할당하는 방법도 있다.
필드명을 지정하는 경우도 있지만, 필드명을 생략할 경우에는 zero value를 갖는다.
(int - 0, float - 0.0, string - "", pointer - nil)
또 다른 객체 생성 방법으로 Go 내장함수 new()를 쓸 수 있다.
new()를 사용하면 모든 필드를 zero value로 초기화 하고 객체의 포인터를 리턴한다.
struct의 필드가 map 타입인 경우, 미리 초기화 해놓으면 외부 struct 사용자가 매번 map을 초기화할 필요가 없다.
Go 메서드는 특별한 형태의 func 함수이다.
메서드는 함수 정의에서 func와 함수명 사이에 그 함수가 어떤 struct를 위한 메서드인지 표시하게 된다.
흔히 receiver로 불리는 이 부분은 메서드가 속한 struct 타입과 struct 변수명을 지정하는데, struct 변수명은 함수 내에서 마치 입력 파라미터처럼 사용된다.
type Rect struct {
width, height int
}
func (r Rect) area() int {
return r.width * r.height;
}
func main() {
rect := Rect{10, 20}
area := rect.area()
fmt.Println(area)
}
struct가 필드들의 집합체라면, interface는 메서드들의 집합체이다.
interface는 타입(type)이 구현해야 하는 메서드 원형(prototype)들을 정의한다.
하나의 사용자 정의 타입이 interface를 구현하기 위해선 단순히 그 인터페이스가 갖는 모든 메서드들을 구현하면 된다.
인터페이스는 struct와 마찬가지로 type문을 사용하여 정의한다.
type Shape interface {
area() float64
perimeter() float64
}
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인 경우, 어떤 타입이든 해당 인터페이스를 구현하기만 하면 모두 입력 파라미터로 사용될 수 있다는 것을 의미한다.
빈 인터페이스(empty interface)는 흔히 인터페이스 타입 으로 불리운다.
빈 인터페이스는 interface{} 와 같이 표현한다.
모든 type을 나타내기 위해 빈 인터페이스를 사용한다.
즉, 빈 인터페이스는 어떠한 타입도 담을 수 있는 컨테이너라고 볼 수 있다.
인터페이스 타입의 x 와 타입 T에 대하여 x.(T) 로 표현했을 때, 이는 x가 nil이 아니며, x는 T 타입에 속한다는 점을 확인(assert) 하는 것으로 이러한 표현을 Type Assertion 이라 부른다.
만약 x가 nil 이거나 x의 타입이 T가 아니라면, 런타임 에러가 발생할 것이고, x가 T 타입인 경우는 T 타입의 x를 리턴한다.