Golang { Syntax int := 6 }

Golang Syntax _6


21. 메소드

package main
import (
    "fmt"
    "math"
)

type Vertex struct {
    X, Y float64
}

func (v *Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
    v := &Vertex{3, 4}
    fmt.Println(v.Abs())
}
  • Go에는 클래스가 없지만 메소드를 구조체(struct)에 붙일 수 있다.
  • 메소드 리시버(method receiver) func 키워드와 메소드 이름 사이에 인자로 들어간다.
    func 구조체이름(혹은포인터) 메소드_리시버_이름 반환타입이고 위 코드를 통해 예시를 들면,
    func (v *Vertex) Abs() float64: 구조체 VertexAbs()란 이름으로 호출 할 수 있다.
  • Go의 메소드는 구조체 뿐만아니라 모든 타입(type)에 붙일 수 있다.
    하지만, 다른 패키지에 있는 타입이나 기본 타입들에 메소드를 붙이는 것은 불가능하다.

    package main
    import (
        "fmt"
        "math"
    )
    
    type MyFloat float64
    
    func (f MyFloat) Abs() float64 {
        if f < 0 {
            return float64(-f)
        }
        return float64(f)
    }
    
    func main() {
        f := MyFloat(-math.Sqrt2)
        fmt.Println(f.Abs())
    }
    
  • 포인터 리시버는 메소드가 호출될때마다 값이 복사되는 것을막고, 메소드에서 리시버 포인터가 가르키는 값을 수정하기 위함이다.
    (함수에서 포인터를 전달하는것과 값을 전달하는 것에 차이를 생각하면 이해가 쉽다..)

    package main
    import (
        "fmt"
        "math"
    )
    
    type Vertex struct {
        X, Y float64
    }
    
    func (v *Vertex) Scale(f float64) {
        v.X = v.X * f
        v.Y = v.Y * f
    }
    
    func (v *Vertex) Abs() float64 {
        return math.Sqrt(v.X*v.X + v.Y*v.Y)
    }
    
    func main() {
        v := &Vertex{3, 4}
        v.Scale(5)
        fmt.Println(v, v.Abs()) // &{15 20} 25
    }
    

 

22. 인터페이스

package main
import (
    "fmt"
    "math"
)

type Abser interface {
    Abs() float64
}

func main() {
    var a Abser

    f := MyFloat(-math.Sqrt2)
    v := Vertex{3, 4}
    a = f  // a MyFloat implements Abser
    a = &v // a *Vertex implements Abser
    //a = v  // a Vertex, does NOT
    // implement Abser

    fmt.Println(a.Abs()) // 5
}

/****** MyFloat ******/
type MyFloat float64

func (f MyFloat) Abs() float64 {
    if f < 0 {
        return float64(-f)
    }
    return float64(f)
}

/****** Vertex ******/
type Vertex struct {
    X, Y float64
}

func (v *Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
  • 인터페이스는 메소드의 집합으로 정의된다.
    인터페이스 안에 정의되어있는 모든 메소드가 구현되어있는 타입의 값은 모두 인터페이스 타입의 값이 될수 있다.
  • 인터페이스의 메소드가 이미 정의 되어있는 패키지 혹은 타입에 대해서 따로 명시적으로 표현할 필요가 없다.
    패키지 io에 Reader와 Writer가 정의되어 있기 때문에 따로 정의할 필요가없다.

    package main
    import (
        "fmt"
        "os"
    )
    
    type Reader interface {
        Read(b []byte) (n int, err error)
    }
    
    type Writer interface {
        Write(b []byte) (n int, err error)
    }
    
    type ReadWriter interface {
        Reader
        Writer
    }
    
    func main() {
        var w ReadWriter
        //var w Writer
    
        // os.Stdout implements Writer
        w = os.Stdout
    
        fmt.Fprintf(w, "hello, writer\n")
    }
    

 

22. 에러

package main
import (
    "fmt"
    "time"
)

type MyError struct {
    When time.Time
    What string
}

func (e *MyError) Error() string {
    return fmt.Sprintf("at %v, %s",
        e.When, e.What)
}

func run() error {
    return &MyError{
        time.Now(),
        "it didn't work",
    }
}

func main() {
    if err := run(); err != nil {
        fmt.Println(err) // at 2009-11-10 23:00:00 +0000 UTC m=+0.000000001, it didn't work
    }
}
  • Go에선 문자열을 반환하는 하나의 메소드 Error()로 구성된 내장 인터페이스 타입 error를 사용한다.
    type error interface {
         Error() string
    }
  • 기본적으로 모든 에러는 error interface를 호출하지만, 위 코드 처럼 직접 에러를 정의할 수 있다.
  • + 이해가 힘들어 추가적인 내용 첨부 [예제로 배우는 Go 프로그래밍]

 

23. 연습문제

Sqrt 함수는 복소수를 지원하지 않기 때문에, 음수가 주어지면 nil 이 아닌 에러 값을 반환해야 합니다.


새로운 타입을 만드십시오.

type ErrNegativeSqrt float64

and make it an error by giving it a 그리고 아래 메소드를 구현함으로써 그 타입이 error 가 되게 하십시오.

func (e ErrNegativeSqrt) Error() string

이는 ErrNegativeSqrt(-2).Error() 가 "cannot Sqrt negative number: -2" 를 반환하는 그러한 메소드입니다.


package main
import (
    "fmt"
    "math"
)

type ErrNegativeSqrt float64

func (e ErrNegativeSqrt) Error() string {
    if e < 0 {
    	return fmt.Sprintf("cannot Sqrt negative number: %0.1f", e)
    }
    return fmt.Sprintf("OK")
}


func Sqrt(f float64) (float64, error) {
    if f > 0 {
	    var a, z, c float64 = 0, 1, 0
	    for a = 1; a < 10; a++ {
	        if math.Abs(z - c) > 1e-10 {
    	        c = z
        	    z = z - (z * z - f)/(2*z)
	        } else {
    	        break
        	}
    	}
    	return z, ErrNegativeSqrt(f)
    } else {
        return f, ErrNegativeSqrt(f)
    }
    
}

func main() {
    fmt.Println(Sqrt(2))
    fmt.Println(Sqrt(-2))
}

 

글의 문제가 있다면 댓글을 달아 주세요.

This site uses Akismet to reduce spam. Learn how your comment data is processed.