구조체는 같은 속성의 필드의 집합체이고, 메소드는 함수 중에서도 구조체의 속성을 기능적으로 수행하는 특별한 함수.
예를들어 구조체로
원의 정보
사각형의 정보
가 있고 이 구조체를 이용해 넓이를 구하는 메소드가있다고 생각해보자,
기능은 같지만(넓이를 구한다) 두 구조체의 필드가 다르고 연산 방법도 다르기 때문에 메소드도 각각의 구조체에 선언해야함. 따라서 이름은 같지만(getArea)내용물이 다른 메소드를 만들어야 함.
package main
import (
"fmt"
"math"
)
type Rect struct {
width, height float64
}
// Rect 의 넓이를 구하는 getArea 메소드
func (r Rect) getArea() float64 {
return r.width * r.height
}
type Circle struct {
radius float64
}
// Circle 의 넓이를 구하는 getArea 메소드
func (c Circle) getArea() float64 {
return math.Pi * c.radius * c.radius
}
func main() {
r1 := Rect{10,20}
c1 := Circle{10}
fmt.Println(r1.getArea())
fmt.Println(c1.getArea())
}
구조체 정보를 이용해 넓이를 구하는 getArea()라는 메소드가 두개 있음.
두 개의 getArea()는 공통적으로 넓이를 구하는 기능을 하지만, 전달받는 구조체 객체와 그에 따른 연산 과정이 다르기 때문에 각각 따로 선언.
(전달받는 구조체가 다르기 때문에 메소드의 이름이 동일해도 상관없음.)
많은 수의 객체의 넓이를 출력하려면?
전달하는 객체의 형이 다르기 때문에 같은 기능의 메소드이기 때문에, 매개변수를 다르게 설정해줘야함.
결론, 같은 속성의 기능을 묶어놓은 인터페이스를 활용하면 인터페이스에 있는 메소드들을 호출 할 수 있음.
package main
import (
"fmt"
"math"
)
// geometry 인터페이스 선언, Rect 와 Circle 구조체의 getArea 메소드를 모두 포함.
type geometry interface {
getArea() float64
}
type Rect struct {
width, height float64
}
// Rect 의 넓이를 구하는 getArea 메소드
func (r Rect) getArea() float64 {
return r.width * r.height
}
type Circle struct {
radius float64
}
// Circle 의 넓이를 구하는 getArea 메소드
func (c Circle) getArea() float64 {
return math.Pi * c.radius * c.radius
}
func main() {
r1 := Rect{10,20}
c1 := Circle{10}
fmt.Println(r1.getArea())
fmt.Println(c1.getArea())
printMeasure(r1, c1)
}
// 인터페이스를 가변인자로 받는 함수
func printMeasure(m ...geometry) {
// 가변 인자 함수의 값은 슬라이스형
for _ , m := range m {
// 인터페이스의 메소드 호출
fmt.Println(m.getArea())
}
}
매개변수로 인터페이스를 사용한다는 것은, 구조체에 관계없이 인터페이스에 포함된 메소드를 사용하겠다는 뜻.
package main
import (
"fmt"
"math"
)
// geometry 인터페이스 선언, Rect 와 Circle 구조체의 getArea 메소드를 모두 포함.
type geometry interface {
// 넓이
getArea() float64
// 둘
getPerimeter() float64
}
type Rect struct {
width, height float64
}
// Rect 의 넓이를 구하는 getArea 메소드
func (r Rect) getArea() float64 {
return r.width * r.height
}
// Rect 의 둘레를 구하는 getPerimeter
func (r Rect) getPerimeter() float64 {
return 2 * (r.width + r.height)
}
type Circle struct {
radius float64
}
// Circle 의 넓이를 구하는 getArea 메소드
func (c Circle) getArea() float64 {
return math.Pi * c.radius * c.radius
}
// Circle 의 둘레를 구하는 getPerimeter
func (c Circle) getPerimeter() float64 {
return 2 * math.Pi * c.radius
}
func main() {
r1 := Rect{10,20}
c1 := Circle{10}
fmt.Println(r1.getArea())
fmt.Println(c1.getArea())
printMeasure(r1, c1)
}
// 인터페이스를 가변인자로 받는 함수
func printMeasure(m ...geometry) {
// 가변 인자 함수의 값은 슬라이스형
for _ , m := range m {
// 인터페이스의 메소드 호출
fmt.Println(m)
fmt.Println(m.getArea())
fmt.Println(m.getPerimeter())
}
}
만약에 어떤 새가 오리처럼 걸어다니고, 헤엄치고, 꽥꽥 소리를 낸다면 나는 그 새를 오리라고 부르겠다.
=> 각 객체의 실제 타입은 상관하지 않고, 구현된 메소드로만 타입을 판단하는 것을 Duck Typing이라고 함!
package main
import "fmt"
// 나는 어떤 물체가 이륙(TakeOff)와 착륙(Landing)을 한다면 나는 그 물체를 비행기(AirPlane)이라고 부르겠다.
type AirPlane interface {
TakeOff()
Landing()
}
type A380 struct {
}
func (a A380) TakeOff() {
fmt.Println("A380 이 이륙합니다.")
}
func (a A380) Landing() {
fmt.Println("A380 이 착륙합니다.")
}
type B777 struct {
}
func (b B777) TakeOff() {
fmt.Println("B777 이 이륙합니다.")
}
func (b B777) Landing() {
fmt.Println("B777 이 착륙합니다.")
}
func inAirPort(a AirPlane) {
a.TakeOff()
a.Landing()
}
func main() {
var KAL123 A380
var AN222 A380
var JN222 B777
var HL222 B777
inAirPort(KAL123)
inAirPort(AN222)
inAirPort(JN222)
inAirPort(HL222)
}
인터페이스도 type(형)으로 사용 할 수 있음.
1. 인터페이스는 내용을 따로 선언하지 않아도 형으로서 사용이 가능.
2. 인터페이스는 하나의 경이기 때문에 매개변수로 사용 가능.
3. 인터페이스는 어떠한 타입도 담을수 있는 컨테이너!(Dynamin type)
package main
import "fmt"
func main() {
var x interface{}
x = 1
println(x)
x = "test"
println(x)
}
func PrintValue(i interface{}) {
fmt.Println(i)
}
인터페이스형으로 선언된 변수는 초기화 하는 값에 따라 형이 자동 명시 되지만,
사실 타입(형)은 Danamic type임.
따라서 확실한 형을 표현하기 위해서는 'Type Assertion'을 할 필요가 있음.
인터페이스에서 형을 선언하기 위해서는 "변수이름.(type)"을 명시해주면 됨.
package main
import "fmt"
func main() {
var num interface{} = 10
a := num
b := num.(int)
fmt.Printf("%T, %d \n",a, a)
printTest(b)
}
func printTest(i interface{}) {
fmt.Printf("%T, %d\n", i, i)
}
package main
import (
"fmt"
"strconv"
)
type Human struct {
name string
age int
}
func main() {
h1 := Human{
name: "show",
age: 30,
}
fmt.Println(formatString(h1))
fmt.Println(formatString(&h1))
fmt.Println(formatString("ss"))
fmt.Println(formatString(1))
fmt.Println(formatString(0.12))
}
// 빈 인터페이스 타입은 함수의 매개변수, 리턴값, 구조체의 필드로 사용할 수 있음.
func formatString(arg interface{}) string {
// 빈 인터페이스를 사용하여 모든 타입을 받음
// typeAssertion
switch arg.(type) {
case int:
i := arg.(int)
return strconv.Itoa(i)
case float32:
f := arg.(float32)
return strconv.FormatFloat(float64(f), 'f', -1, 32)
case float64:
f := arg.(float64)
return strconv.FormatFloat(f, 'f', -1, 64)
case string:
s := arg.(string)
return s
case Human:
p := arg.(Human)
return p.name + " " + strconv.Itoa(p.age)
case *Human:
p := arg.(*Human)
return p.name + " " + strconv.Itoa(p.age)
default:
return "Error"
}
}