메서드
- Go도 사용자 정의 타입에 대한 메서드를 지원한다.
- 하지만, 메서드 이름은 오버로드 되지 않는다. → 상속 x
- 아래 코드에서 Person(메서드의 주체)를 리시버 라고 부른다.
func (p Person) String() string {
// logic
}
- 메서드도 함수와 유사하다. 다음이 가능하다.
type Adder struct {
start int
}
func (a Adder) AddTo(val int) int {
return a.start + val
}
func main() {
myAdder := Adder{
start: 10,
}
f1 := myAdder.AddTo // 메서드를 변수에 할당
fmt.Println(f1(5)) // 15
f2 := Adder.AddTo // 여기서 첫 번째 파라미터는 Adder의 리시버이다.
fmt.Println(f2(myAdder, 10)) // 20
}
포인터 리시버와 값 리시버
- 포인터 타입의 파라미터를 이용해 파라미터가 함수에서 수정될 수 있다.
- 다음 경우들에 따라 포인터 리시버와 값 리시버를 구분하여 사용한다.
- 메서드가 리시버를 수정한다면, 반드시
포인터 리시버를
사용한다. - 메서드가 nil을 처리할 필요가 있다면, 반드시
포인터 리시버를
사용한다. - 메서드가 리시버를 수정하지 않는다면,
값 리시버를
사용할 수 있다.
type Counter struct {
total int
lastUpdated time.Time
}
// 포인터 리시버
func (c *Counter) Increment() {
c.total++
c.lastUPdated = time.Now()
}
// 값 리시버
func (c Counter) String() {
return fmt.Sprintf("total %d, last updated: %d", c.total, c.lastUpdated)
}
- Go의 경우, 인터페이스를 충족시키기 위한 경우가 아니라면 구조체의
Getter, Setter
는 작성하지 않는다. → 각 항목에 직접 접근하는 것을 권장한다.
열거형을 위한 iota
- Go는 열거형을 가지고 있지 않다.
- 대신
iota
를 사용하여 증가하는 값을 상수 세트에 할당할 수 있다. - 첫 번째 상수는 0, 두 번째는 1 …
type MailCategory int
const (
Uncategorized MailCategory = iota // 0
Personal // 1
Spam // 2
Social // 3
Advertisements // 4
)
인터페이스
- 인터페이스를 만족시키기 위한 구체 타입에서 반드시 구현해야 할 메서드들이 나열된다.
type Stringer interface {
String() string
}
- 인터페이스는 암묵적으로 구현이 된다.
- 구체 타입은 인터페이스를 선언하지 않는다.
- 인터페이스를 위한 메서드 세트의 모든 메서드들을 포함한다.
- (자바 같은 경우는, 인터페이스 정의하고 구현을 생성해야 한다.)
- 이렇게 하면 구현이 아닌 동작에 집중하여 필요에 따라 구현을 바꿀 수 있다.
- Go - Duck Typing
- 인터페이스를 받고 구조체 반환하기
- 함수로 실행되는 비즈니스 로직은 인터페이스를 통해 실행되어야 하지만, 함수의 출력은 구체 타입이어야 한다.
- 의존성을 부여하고, Versioning을 하기 위해
- 빈 인터페이스
interface{}
- 변수가 어떤 타입의 값이라도 저장할 수 있다.
- 일반적으로 JSON 파일과 같이 외부에서 온 불확실한 스키마의 데이터에 대한 PlaceHolder로 사용한다.
- 인터페이스는 의존성 주입을 쉽게 만든다
- 의존성 주입 → 해당 작업 수행이 필요한 기능을 명시적으로 코드에 지정할 수 있는 개념
Type Assertion과 Type Switching
- Type Assertion
- 인터페이스를 구현한 구체 타입의 타입을 지정하는 것
var i interface{}
var mine MyInt=20
i = mine
i2 := i.(MyInt)
- 이를 암묵적 형 변환을 허용하지 않는다고 한다. go는 명시적 형 변환만 지원한다.
func main() {
var a interface{} = 1.0
i := a.(int)
fmt.Println(i)
}
// panic: interface conversion: interface {} is float64, not int
- Type switch
- 인터페이스를 여러 가능한 타입 중 하나로 사용할 때, switch를 활용한다.
func doThings(i interface{}) {
switch j := i.(type) {
case nil:
case int:
case string:
case bool:
default:
}
}
.(type)
을 명시한다.Share article