{Go} 12. struct / 13. interface

Jerry·2021년 6월 19일
0

12. struct

  • 객체지향적인 추상화를 구조체로 정의한다
  • 자바의 클래스와 유사하지만 필드만 있고 매소드는 갖지 않음
    => 리시버를 통해 매소드를 구조체와 연결

기본 사용

    // 구조체 선언
    type Account struct {
        number   string
        balance  float64
        interest float64
    }

    // 구조체에 리시버로 매소드 연결
    func (a Account) Calculate() float64 {
        return a.balance + (a.balance * a.interest)
    }

    kim := Account{number: "245-901", balance: 10000000, interest: 0.015}
    // 인자 생략 가능
    lee := Account{number: "245-902", balance: 20000000}
    // 인자 명칭 생략 가능(순서대로 할당해야함)
    cho := Account{"245-904", 15000000, 0.4}

    // 리시버를 이용해 연결된 매소드 사용
    fmt.Println(int(kim.Calculate()))

구조체 포인터 선언

  • 구조체 포인터 선언방법 3가지
	// 선언 방법1
	var kim *Account = new(Account)
	kim.number = "245-901"
	kim.balance = 1000000000
	kim.interest = 0.34

	// 선언 방법2
	hong := &Account{number: "245-902", balance: 15000000, interest: 0.213}

	// 선언 방법3
	lee := new(Account)
	lee.number = "245-903"
	lee.balance = 213434233
	lee.interest = 0.213434233
  • 리시버인 경우에는 매소드에 선언한 구조체 타입이 포인터형인 경우, 실제 생성 객체가 포인터형이 아니어도 자동으로 주소값을 넘겨주도록 변환이 되는데
type shoppingBasket struct{ cnt, price int }

func (b *shoppingBasket) rePurchaseP(cnt, price int) {
	b.cnt += cnt
	b.price += price
}

bk := shoppingBasket{3, 5000}
	fmt.Println(bk.rePurchaseP(5,1000))
bkPointer := &shoppingBasket{3, 5000}
	fmt.Println(bkPointer.rePurchaseP(5,1000))
  • 매소드의 입력 인자로 구조체가 들어가는 경우에는 실제 매소드 호출시 넣는 구조체 타입이 반드시 포인터형 이어야함
    type shoppingBasket struct{ cnt, price int }

    func rePurchaseP(b *shoppingBasket, cnt int, price int) {
	b.cnt += cnt
	b.price += price
    }

    //bk := shoppingBasket{3, 5000}
    //fmt.Println(rePurchaseP(bk,5,1000)) // bk는 포인터형이 아니므로 불가함
    bkPointer := &shoppingBasket{3, 5000}
    fmt.Println(rePurchaseP(bk,5,1000))

구조체 익명 선언

	// 단건 선언
        car1 := struct{ name, color string }{"520d", "red"}
        fmt.Println("ex1 : ", car1)
        
	// 구조체 배열 선언
	cars := []struct{ name, color string }{{"520d", "red"}, {"530i", "white"}, {"528i", "blue"}}
	
	for _, c := range cars {
		fmt.Printf("(%s, %s) --- (%#v) \n", c.name, c.color, c)
	}

reflect

      import (
          "fmt"
          "reflect"
      )

      type Car struct {
          name    string "차량명"
          color   string "색상"
          company string "제조사"
 // 순서 : tag.Field(i).Name tag.Field(i).Type tag.Field(i).Tag
      }

      tag := reflect.TypeOf(Car{})
      
	for i := 0; i < tag.NumField(); i++ {
		fmt.Println(tag.Field(i).Tag, tag.Field(i).Name, tag.Field(i).Type)
	}

중첩 구조체

      type Car struct { 
          name    string "차량명"
          color   string "색상"
      }

      type spec struct { // 첫 글자 소문자 => private 접근제어
          length int "전장"
          height int "전고"
          width  int "전축"
      }

      func main() {
	car1 := Car{
		"520d",
		"silver",
		spec{4000, 1000, 2000},
	}
    

13. interface

  • 객체의 동작을 표현하고 골격을 잡음
  • 추상화 구현

인터페이스 기본

        // 동물의 행동을 인터페이스로 선언
        type Behaivor interface {
            bite()
        }

        // Dog 구조체
        type Dog struct {
            name   string
            weight int
        }

        // Dog 구조체에 리시버로 bite를 구현
        func (d Dog) bite() {
            fmt.Println(d.name, "bites!")
        }

    // [인터페이스 연결 방법 1]
	// Dog 객체 생성
        dog1 := Dog{"poll", 10}
	// 인터페이스 초기화 : Dog에 bite 리시버가 있으므로 할당 가능
        var interface1 Behaivor = dog1
        // Dog에 리시버로 bite를 구현해 놓았으므로 호출 가능
	interface1.bite()
	dog1.bite()

    // [인터페이스 연결 방법 2]
    	dog2 := Dog{"marry", 12}
	interface2 := Behaivor(dog2)
	interface2.bite()

    // [인터페이스 연결 방법 3] - 슬라이스
    	interface3 := []Behaivor{dog1, dog2}

	// 인덱스 형태로 실행
	for idx, _ := range interface3 {
		interface3[idx].bite()
	}

	// 값 형태로 실행(인터페이스)
	for _, val := range interface3 {
		val.bite()
	}

duck typing : 덕 타이핑

오리처럼 걷고, 소리내고 등 행동을 오리처럼 하면 오리라고 판단할수 있다.

  • 구조체 및 변수의 값이나 타입에 상관없이 오로지 구현 매소드로만 판단
    type Behaivor interface {
        bite()
        sounds()
        run()
    }

    func act(animal Behaivor) {
        animal.bite()
        animal.sounds()
        animal.run()
    }

    // 익명 인터페이스
    func actAnonymous(animal interface{ run() }) { 
        animal.run()
    }

    // Dog과 Cat 구조체는 각각 bite(), sounds(), run() 리시버를 구현해 놓았음을 가정
    dog := Dog{"ppoya", 10}
    cat := Cat{"poll", 2}

    // 구조체를 넘기지만 함수에서 Behaivor 인터페이스에 대해 다룰 수 있음
    // Behaivor 인터페이스 매소드 중 하나라도 구현하지 않았으면 act 매소드의 인자로 전달할 수 없음
    act(dog)
    act(cat)
    // 익명 인터페이스에 전달
    actAnonymous(dog)
    actAnonymous(cat)

빈 인터페이스 활용

함수 내에서 어떠한 타입이라도 유연하게 매개변수로 받을 수 있음
=> 동적으로 타입이 정해짐

        func printValue(s interface{}) {
            fmt.Println("ex 1 :", s)
        }

	dog := Dog{"poll", 10}
	cat := Cat{"bob", 5}

	printValue(dog)
	printValue(cat)
	printValue(15)

인터페이스 형변환

인터페이스객체.(타입) 으로 형변환이 가능함

var a interface{} = 15
b := a.(int)

동적으로 입력받은 인터페이스의 타입에 따라 분기 처리에 응용

        func checkType(arg interface{}) {
            // arg.(type) 을 통해서 현재 데이터형 반환
            switch arg.(type) {
            case bool:
                fmt.Println("this is a bool", arg)
            case int, int8, int16, int32, int64:
                fmt.Println("this is int", arg)
            case float64:
                fmt.Println("this is float", arg)
            case string:
                fmt.Println("this is string", arg)
            case nil:
                fmt.Println("this is nil", arg)
            }
        }

	checkType(true)
	checkType(1)
	checkType(22.542)
	checkType(nil)

참고자료

[학습 자료] 인프런 - 쉽고 빠르게 끝내는 GO언어 프로그래밍 핵심 기초 입문 과정
[공식사이트] https://golang.org/tutorial

profile
제리하이웨이

0개의 댓글