2024.03.19
type Person struct {
FirstName string
LastName string
Age int
}
type Score int
type Converter func(string)Score
type TeamScores map[string]Score
type Person struct {
FirstName string
LastName string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%s %s, age %d", p.FirstName, p.LastName, p.Age)
}
func
키워드와 메서드 이름 사이에 들어간다.this
나 self
를 사용하는 것은 관용적이지 못하다.nil
인스턴스를 처리할 필요가 있다면, 반드시 포인터 리시버를 사용해야 한다.// 하나는 값 리시버를 사용하고 다른 하나는 포인터를 리시버를 사용하는 두 가지 메서드가 있는 타입
type Counter struct {
total int
lastUpdated time.Time
}
func (c *Counter) Increment() {
c.total++
c.lastUpdated = time.Now()
}
func (c Counter) String() string {
return fmt.Sprintf("total: %d, last updated: %v", c.total, c.lastUpdated)
}
// 실행하는 코드
var c Counter
fmt.Println(c.String())
c.increament()
fmt.Println(c.String())
// 출력 결과
// total: 0, last updated: 0001-01-01 00:00:00 +0000 UTC
// total: 1, last updated: 2009-11-10 23:00:00 +0000 UTC m=+0.000000001
c
가 값 타입에도 불구하고 포인터 리시버로 메서드를 호출할 수 있다는 것이다.c.Increment()
→ (&c).Increment()
nil
인스턴스로 메서드를 호출하면 패닉이 발생한다.nil
인스턴스의 가능성을 처리하면 제대로 동작할 것이다.nil
파라미터와 같이 포인터의 복사본이 변경되면 원본은 변경이 되지 않는다.nil
을 처리하고 원본 포인터를 nil
이 아닌 것으로 만드는 포인터 리시버 메서드를 작성할 수 없다는 의미이다.nil
리시버를 처리할 수 없다면, nil
을 확인하고 오류를 반환하도록 하자type Adder struct {
start int
}
func (a Adder) AddTo(val int) int {
return a.start + val
}
// 일반적인 방법으로 인스턴스의 메서드 실행
myAddr := Adder{start: 10}
fmt.Println(myAdder.AddTo(5)) // 15를 출력
// 메서드를 변수에 할당
f1 := myAdder.AddTo
fmt.Println(f1(10)) // 20을 출력
f2 := Adder.AddTo
fmt.Println(f2(myAdder, 15)) // 25를 출력
type HighScore Score
type Employee Person
var i int = 300
var s Score = 100
var hs HighScore = 200
hs = s // 컴파일 오류!
s = i // 컴파일 오류!
s = Score(i) // ok
hs = HighScore(s) // ok
int
타입 대신에 Percentage
타입을 사용할 때 누군가가 코드를 읽는다면 더 명확할 수 있고, 유효하지 않는 값으로 해당 메서드를 실행하는 것을 어렵게 한다.iota
를 사용하여 증가하는 값을 상수 세트에 할당할 수 있도록 한다.iota
를 사용할 때 먼저 모든 유효한 값을 나타내는 정수 기반의 타입을 정의하는 방법이 ㅣ가장 좋다.type MailCategory int
const (
Uncategorized MailCategory = iota
Personal
Spam
Social
Advertisements
)
const
블록에서 첫 번째 상수는 지정된 타이비을 가지고 값으로 iota
로 설정했다. 후속 라인에는 타입이나 값이 지정되지 않았다.iota
의 값을 증가시킨다.iota
기반의 열거는 값 세트를 구별할 수 있는지를 관리할 수 있을 때만 의미가 있고, 그 뒤에 숨겨진 값이 무엇인지는 특별히 신경 쓰지 않는다. 실제 값이 중요한 경우는 명시적으로 지정해야 한다.type Employee sturct {
Name string
ID string
}
func (e Employee) Description() string {
return fmt.Sprintf("%s (%s)", e.Name, e.ID)
}
type Manager struct {
Employee
Reports []Employee
}
func (m Manager) FindNewEmployees() []Employee {
// do business logic
}
Manager
가 Employee
타입의 항목을 포함하고 있지만, 해당 항목에 이름이 지정디어 있지 않다.Employee
를 임베딩한 것이다. 임베딩된 항목의 선언된 모든 항목이나 메서드는 승격(promotion)되어 구조체를 포함하고 바로 실행도 가능하다.m := Manager{
Employee: Employee{
Name: "Bob Bobson",
ID: "12345",
},
Reports: []Employee{},
}
fmt.Println(m.ID) // 12345 출력
fmt.Println(M.Description()) // Bob Bobson (12345) 출력
type Inner struct {
X int
}
type Outer struct {
Inner
X int
}
o := Outer{
Inner: Inner{
X: 10,
},
X: 20,
}
fmt.Println(o.X) // 20
fmt.Println(o.Inner.X) // 10
type
키워드를 사용한다.// fmt 패키지에 Stringer 인터페이스의 정의이다.
type Stringer interface {
String() string
}
er
을 붙인다.type LogicProvider struct {}
func (lp LogicProvider) Process(data string) string {
// business logic
}
type Logic interface {
Process(data string) string
}
type Client struct {
L Logic
}
func (c Client) Program() {
// get data from somewhere
c.L.Process(data)
}
main() {
c := Client{
L: LogicProvider{},
}
c.Program()
}
type Reader interface {
Read(p []byte) (n int, err error)
}
type Closer interface {
Close() error
}
type ReadCloser interface {
Reader
Closer
}
nil
은 인터페이스 인스턴스의 제로 값으로도 사용할 수 있지만, 구체 타입을 위해 사용하는 것보다는 단순하지가 않다.nil
이라는 것은 타입과 값 모두 nil
이어야 한다는 것이다.var s *string
fmt.Println(s == nil) // true
var i interface{}
fmt.Println(i == nil) // true
i = s
fmt.Println(i == nil) // false
nil
이 아닌 한, 인터페이스도 nil
이 아니다.nil
이 아닌 인터페이스 인스턴스는 nil
과 같지 않기 때문에, 타입이 nil
이 아닐 때 인터페이스와 연관된 값이 nil
인지 여부를 말하는 것은 간단치 않다. 반드시 리플렉션을 사용하여 알아봐야 한다.interface{}
를 사용하여 이것을 표현한다.var i interface{}
i = 20
i = "hello"
i = struct {
FirstName string
LastNmae string
} {"Fred", "Fredson"}
interface{}
의 다른 용도는 사용자 생성 데이터 구조 내에 값을 저장하는 방법으로 사용한다. Go에서 현재 사용자 정의된 제네릭이 없기 때문이다. 슬라이스, 배열, 맵 이외의 데이터 구조가 필요하고 단일 타입에서만 동작하지 않도록 하려면 해당 값을 들고 있기 위해 interface{}
타입의 항목을 사용해야 한다.type LinkedList sturct {
Value interface{}
Next * LinkedList
}
func (ll *LinkedList) Insert(pos int, val interface{}) *LinkedList {
if ll == nil || pos == 0 {
return &LinkedList{
Value: val,
Next: ll,
}
}
ll.Next = ll.Next.Insert(pos-1, val)
return ll
}
type MyInt int
func main() {
var i interface{}
var mine MyInt = 20
i = mine
i2 := i.(MyInt)
fmt.Println(i2 + 1)
}
i2 := i.(string)
fmt.Println(i2)
// panic: interface conversion: interface {} is main.MyInt, not string
i2 := i.(string)
fmt.Println(i2 + 1)
// panic: interface conversion: interface {} is main.MyInt, not int
i2, ok := i.(int)
if !ok {
return fmt.Errorf("unexpected type for %v", i)
}
fmt.Println(i2 + 1)
func doThings(i interface{}) {
switch j := i.(type) {
case nil:
// i는 nil이다. j는 interface{} 타입이다.
case int:
// j는 정수다.
case MyInt:
// j는 MyInt 타입이다.
case io.Reader:
// j는 io.Reader 타입이다.
case string:
// j는 문자열이다.
case boo, rune:
// i가 불리언이거나 룬일 수 있기에 j는 interface{} 타입이다.
default:
// i가 무슨 타입인지 알 수 없기에 j는 interface{} 타입이다.
}
}
타입 스위치의 목적은 이미 존재하는 변수를 새로운 변수로 파생시키는 것이기 때문에, 전환되는 변수를 같은 이름의 변수로(
i := i.(type)
할당하는 것은 관용적이고 섀도잉이 좋게 쓰이는 몇 안되는 것 중 하나이다.
io.Copy
함수를 호출 했을 때, 더 효과적으로 복사할 수 있또록 한다. 이 함수는 io.Writer
와 io.Reader
타입의 파라미터를 받아 해당 작업을 수행하기 위해 io.copyBuffer
함수를 호출한다. io.Reader
파라미터가 io.WriterTo
를 구현했거나 io.Writer
파라미터가 io.ReaderFrom
을 구현했다면 해당 함수의 대부분의 작업은 하지 않고 넘어갈 수 있다.func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
// reader가 WriteTo 메서드를 가지고 있다면, 복사를 위해 해당 함수를 사용한다.
// 할당과 복사 방지
if wt, ok := src.(WriterTo); ok {
return wt.WriteTo(dst)
}
// 비슷하게, writer가 ReadFrom 메서드를 가지고 있다면 복사를 위해 하당 함수를 사용한다.
if rt, ok := dst.(ReadFrom); ok {
return rt.ReadFrom(src)
}
// 함수 하단 구현부..
}
// HTTP 핸들러는 HTTP 서버 요청을 처리하는 것에 대한 인터페이스
type Handler interface {
ServerHTTP(http.ResponseWriter, *http.Request(
}
// 타입 변환을 사용하여 http.HandlerFunc로 변환하여
// 아래 시그니처를 가지는 모든 함수를 http.Handler로써 사용이 가능하다.
type HandlerFunc func(http.ResposeWriter, *http.Request)
func (f HandlerFunc) serverHTTP(w http.ResponseWriter, r *http.Request) {
f(w, r)
}