Learning Go - Chapter 7. 타입, 메서드, 인터페이스

choko's avatar
Jun 29, 2024
Learning Go - Chapter 7. 타입, 메서드, 인터페이스
 
 
 

메서드

  • 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 }
 

포인터 리시버와 값 리시버

  • 포인터 타입의 파라미터를 이용해 파라미터가 함수에서 수정될 수 있다.
  • 다음 경우들에 따라 포인터 리시버값 리시버를 구분하여 사용한다.
      1. 메서드가 리시버를 수정한다면, 반드시 포인터 리시버를 사용한다.
      1. 메서드가 nil을 처리할 필요가 있다면, 반드시 포인터 리시버를 사용한다.
      1. 메서드가 리시버를 수정하지 않는다면, 값 리시버를 사용할 수 있다.
      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)
    • 이 경우, 타입이 잘못됐으면 panic을 일으킨다.
    • float를 int로 type assertion 시키면 panic이 일어난다.
      • 이를 암묵적 형 변환을 허용하지 않는다고 한다. 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

Tom의 TIL 정리방