Runtime 도중 인터페이스나 구조체의 타입을 확인하기 위해서 사용할 수 있는 문법 중 하나인 reflect에 대한 다양한 사용 방법을 정리해보자.
reflect에는 2개의 타입이 존재하는데 Type, Value 이 두가지가 존재한다. 두 타입은 interface 변수에 대한 접근을 가능하게 한다. 각 타입을 얻는데는 reflect.TypeOf
, reflect.ValueOf
가 사용된다.
Code
package main
import (
"fmt"
"reflect"
)
func main() {
var x float64 = 3.141592
fmt.Println("type:", reflect.TypeOf(x))
fmt.Println("type:", reflect.ValueOf(x))
}
Print:
type: float64
value: 3.141592
reflect.TypeOf()
, reflect.ValueOf()
모두 인자로 any ( interface{} ) 타입을 받게 된다.
func TypeOf(i any) Type
func ValueOf(i any) Value
Value Type은 Interface() 함수를 통해서 interface{} 타입으로 다시 되돌릴 수 있다.
Code
func main() {
var x float64 = 3.141592
v := reflect.ValueOf(x)
c := v.Interface()
if x == c {
fmt.Println("Same")
}
}
Same
reflection을 통해 값을 받아오고 나서 변경을 하기 위해서는 주소값을 전달해줘야만 가능하다.
코드로 직접 보면 이해가 빠른데
Code
func main() {
var x float64 = 3.141592
// 주소값이 아닌 값을 그대로 전달하면 에러 발생
v_1 := reflect.ValueOf(x)
v_1.SetFloat(1.414)
}
panic: reflect: reflect.Value.SetFloat using unaddressable value
unaddressable value 라는 에러를 발생시킨다. 값을 변경하고 싶다면 x의 address 값을 전달해주어야 한다. address를 전달하고 나서 코드를 실행해도 panic이 발생하게 되는데, 예를들어 포인터가 가리키는 주소의 값을 변경하기 위해서는 *를 사용해주어야 한다. 비슷하게 reflect의 Value에서는 Elem()
함수를 이용해서 값을 변경할 수 있다.
Code
func main() {
var x float64 = 3.141592
// 주소값이 아닌 값을 그대로 전달하면 에러 발생
v := reflect.ValueOf(&x)
p := v.Elem()
p.SetFloat(1.414)
fmt.Println(x)
}
1.414
struct의 field값을 변경하는 방법은 똑같이 ValueOf()
에 주소 값을 전달한다. 값을 변경하기 위해서는 Value
의 Field
, FieldByName
과 같은 함수를 통해서 변경이 가능하다. Field
는 struct에 있는 field 에 대해서 선언된 순서대로 접근이 가능하고 FieldByName
은 field의 변수명을 통해서 접근이 가능하다.
Code
func main() {
person := Human{"Jihwan", 23}
person_value := reflect.ValueOf(&person).Elem()
typeOfHuman := person_value.Type()
for i := 0; i < person_value.NumField(); i++ {
f := person_value.Field(i)
fmt.Printf(
"%d: %s %s = %v\n", i, typeOfHuman.Field(i).Name, f.Type(), f.Interface(),
)
}
person_value.Field(0).SetString("Kim Jihwan")
person_value.Field(1).SetInt(30)
for i := 0; i < person_value.NumField(); i++ {
f := person_value.Field(i)
fmt.Printf(
"%d: %s %s = %v\n", i, typeOfHuman.Field(i).Name, f.Type(), f.Interface(),
)
}
person_value.FieldByName("Name").SetString("Kim")
person_value.FieldByName("Age").SetInt(25)
for i := 0; i < person_value.NumField(); i++ {
f := person_value.Field(i)
fmt.Printf(
"%d: %s %s = %v\n", i, typeOfHuman.Field(i).Name, f.Type(), f.Interface(),
)
}
}
0: Name string = Jihwan
1: Age int = 23
0: Name string = Kim Jihwan
1: Age int = 30
0: Name string = Kim
1: Age int = 25
Struct에 대한 field를 변경할 때 중요한 점은 field의 첫 문자는 대문자일 경우에만 변경이 가능하다는 점이다.