- 책 또는 웹사이트의 내용을 복제하여 다른 곳에 게시하는 것을 금지합니다.
- 책 또는 웹사이트의 내용을 발췌, 요약하여 강의 자료, 발표 자료, 블로그 포스팅 등으로 만드는 것을 금지합니다.
인터페이스 사용하기
이재홍 http://www.pyrasis.com 2014.12.17 ~ 2015.02.07
빈 인터페이스 사용하기
인터페이스에 아무 메서드도 정의되어 있지 않으면 모든 타입을 저장할 수 있습니다.
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!"))
}
1
2.5
Hello, world!
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입니다.
저작권 안내
이 웹사이트에 게시된 모든 글의 무단 복제 및 도용을 금지합니다.- 블로그, 게시판 등에 퍼가는 것을 금지합니다.
- 비공개 포스트에 퍼가는 것을 금지합니다.
- 글 내용, 그림을 발췌 및 요약하는 것을 금지합니다.
- 링크 및 SNS 공유는 허용합니다.