3. 빈 인터페이스 사용하기
페이지 정보
작성자 관리자 댓글 0건 조회 1,705회 작성일 21-07-20 13:48본문
3. 빈 인터페이스 사용하기
인터페이스에 아무 메서드도 정의되어 있지 않으면 모든 타입을 저장할 수 있습니다.
func f1(arg interface{}) { // 모든 타입을 저장할 수 있음
}
함수의 매개변수를 arg interface{}처럼 지정하여 빈 인터페이이스를 받도록 하였습니다.
다음과 같이 빈 인터페이스는 Any로 표현할 수 있습니다.
type Any interface{} // 인터페이스에 메서드를 지정하지 않음
func f2(arg Any) { // 모든 타입을 저장할 수 있음
}
빈 인터페이스 타입은 함수의 매개변수, 리턴값, 구조체의 필드로 사용할 수 있습니다.
이제 모든 타입을 받아서 내용을 출력하는 함수를 만들어보겠습니다.
package main
import (
"fmt"
"strconv"
)
// ↓ 빈 인터페이스를 사용하여 모든 타입을 받음
func formatString(arg interface{}) string {
// ↓ 인터페이스에 저장된 타입에 따라 case 실행
switch arg.(type) {
case int: // arg가 int형이라면
i := arg.(int) // int형으로 값을 가져옴
return strconv.Itoa(i) // strconv.Itoa 함수를 사용하여 i를 문자열로 변환
case float32: // arg가 float32형이라면
f := arg.(float32) // float32형으로 값을 가져옴
return strconv.FormatFloat(float64(f), 'f', -1, 32)
// strconv.FormatFloat 함수를 사용하여 f를 문자열로 변환
case float64: // arg가 float64형이라면
f := arg.(float64) // float64형으로 값을 가져옴
return strconv.FormatFloat(f, 'f', -1, 64)
// strconv.FormatFloat 함수를 사용하여 f를 문자열로 변환
case string: // arg가 string이라면
s := arg.(string) // string으로 값을 가져옴
return s // string이므로 그대로 리턴
default:
return "Error"
}
}
func main() {
fmt.Println(formatString(1))
fmt.Println(formatString(2.5))
fmt.Println(formatString("Hello, world!"))
}
실행 결과
[jklee@www interface_test]$ go run main.go
1
2.5
Hello, world!
[jklee@www interface_test]$
formatString 함수의 매개변수를 보면 타입을 interface{}로 지정하였습니다.
이렇게 하면 모든 타입을 처리할 수 있습니다.
* 인터페이스에 저장된 타입을 알아내려면 switch 분기문 안에서 arg.(type)처럼 인터페이스 변수에 .(type)을 사용합니다.
단 이 방법은 switch 분기문 안에서만 사용할 수 있고, 일반적인 방법으로는 사용할 수 없습니다.
* 타입에 따라 case로 나눕니다.
* 빈 인터페이스 변수는 그대로 사용할 수 없으므로 arg.(int), arg.(float32), arg.(float64), arg.(string)처럼 타입을 지정하여 값을 가져옵니다.
이렇게 타입을 원하는 형태로 바꾸는 작업을 Type assertion이라고 합니다.
* 각 타입에 맞게 strconv.Itoa, strconv.FormatFloat 함수를 사용하여 값을 문자열로 만듭니다.
string의 값은 문자열이므로 변환하지 않고 그대로 리턴합니다.
일반 자료형뿐만 아니라 구조체 인스턴스 및 포인터도 빈 인터페이스로 넘길 수 있습니다.
package main
import (
"fmt"
"strconv"
)
type Person struct { // Person 구조체 정의
name string
age int
}
func formatString(arg interface{}) string {
switch arg.(type) {
case Person: // arg의 타입이 Person이라면
p := arg.(Person) // Person 타입으로 값을 가져옴
return p.name + " " + strconv.Itoa(p.age) // 각 필드를 합쳐서 리턴
case *Person: // arg의 타입이 Person 포인터라면
p := arg.(*Person) // *Person 타입으로 값을 가져옴
return p.name + " " + strconv.Itoa(p.age) // 각 필드를 합쳐서 리턴
default:
return "Error"
}
}
func main() {
fmt.Println(formatString(Person{"Maria", 20}))
fmt.Println(formatString(&Person{"John", 12}))
var andrew = new(Person)
andrew.name = "Andrew"
andrew.age = 35
fmt.Println(formatString(andrew))
}
실행 결과
Maria 20
John 12
Andrew 35
switch arg.(type) { }으로 인터페이스에 저장된 타입을 알아낸 뒤 각 구조체 타입별로 case를 만들어 처리합니다.
여기서 구조체를 그대로 넘겨줬다면 case Person:으로 처리하고, 구조체의 포인터를 넘겨줬다면 case *Person:으로 처리합니다.
마찬가지로 구조체일 때는 arg.(Person), 포인터일 때는 arg.(*Person)으로 값을 가져옵니다.
인터페이스에 저장된 타입이 특정 타입인지 검사하려면 다음과 같이 사용합니다.
var t interface{}
t = Person{"Maria", 20}
if v, ok := t.(Person); ok {
fmt.Println(v, ok)
}
실행 결과
{Maria 20} true
인터페이스.(타입) 형식입니다.
첫 번째 리턴값은 해당 타입으로 된 값이며 두 번째 리턴값은 타입이 맞는지 여부입니다.
타입이 일치하면 true 그렇지 않으면 false입니다.
댓글목록
등록된 댓글이 없습니다.