대표 사진!

 golang은 c++처럼 클래스, 객체, 상속을 지원하지 않습니다.

 대신에 구조체와 메서드를 통해서 Custom타입을 정의하는 것을 지원합니다.  

구조체 

 go에서 구조체는 타입이 필드만으로 구성되어 있습니다. c++ 메서드를 사용하려면 외부에서 선언해야 합니다.

 외부에서 Struct를 사용하게 하려면 Struct의 첫단어를 대문자로 선언해야 합니다

// User에 대한 구조체 정보
// User에 대한 필드 정보만으로 구성됩니다.
type User struct{
    userSN 	int64
    name 	string
    age		int16
} 

func PrintUser(u User) {
    println(u.userSN, u.name, u.age)
}

func main() {
    // User 구조체를 선언합니다.
    var user1 User
    // 구조체에 값을 대입합니다. 
    user1 = User{1, "a", 20}
    PrintUser(user1)
    
    // 구조체를 리터럴 타입으로 선언 및 대입합니다.
    user2 := User{userSN: 2, name: "b", age: 15}
    PrintUser(user2)
}

 구조체 객체를 생성하는 방법은 예제의 user1 처럼 객체를 선언 후 "구조체명{필드값}" 를 대입해서 생성 할 수 있고

 user2처럼 선언과 동시에  ":= 구조체명{필드명:필드값...}" 형식으로 생성 할 수 있습니다.

 

puser := new(User)
puser.userSN = 3
puser.name = "c"
puser.age = 10
PrintUser(*puser)

 구조체는 new 함수를 통해서도 가능합니다. new 함수를 통해서 구조체를 할당 받으면 zero값으로 셋팅된 포인터를 

 전달 받습니다. go에서는 c++과는 다르게 포인터도 .(dot)을 통해서 멤버 변수에 접근합니다. 

// new 내장 함수는 메모리를 할당합니다. 첫번째 인자는 type이고 
// 값이 아닙니다. 
// 리턴 값은 인자로 전달된 타입을 zero값 새로 할당한 포인터 입니다.  
func new(Type) *Type

 new 함수에 대한 설명입니다.

메서드

 구조체에서 설명 했듯이 go에는 class가 존재하지 않습니다. 하지만 해당 구조체 타입의 메서드를 정의 할 수 있습니다.

// User에 대한 구조체 정보
// User에 대한 필드 정보만으로 구성됩니다.
type User struct {
    userSN int64
    name   string
    age    int16
}

// SetName 함수는 User의 name을 설정합니다.
func (u *User) SetName(name string){
    u.name = name
}

// Getname 함수는 User의 name을 리턴합니다.
func (u User) Getname() string{
    return u.name
}

// PrintUser 함수는 User의 정보를 출력합니다. 
func (u User) PrintUser() {
    println(u.userSN, u.name, u.age)
}

func main() {

    // User 구조체를 선언합니다.
    var user1 User
    // 구조체에 값을 대입합니다.
    user1 = User{1, "a", 20}
    user1.SetName("jj")
    user1.PrintUser()
}

 메서드의 정의는 함수의 정의와 비슷하지만 func키워드와 함수명 사이에 어떤 타입의 메서드인지 명시 해야합니다.

 "func (메서드변수 메서드타입) 함수명(파라미터) 리턴값" 방식으로 선언하게 됩니다. 

 go에서는 메서드의 정의 부분을 receiver라고 부릅니다. 

 예제에서는 User 구조체의 메서드들이 정의되어 있는데요

 func과 함수 명 사이에 (u User) 라는 receiver 추가되어 있습니다.

 이렇게 정의하고 나면 구조체에서 직접 해당 메서드를 호출할 수 있습니다.

value receiver & pointer receiver 

 메서드를 정의할 때 receiver를 함수의 파라미터와 마찬가지로 value와 pointer 타입으로 정의 할 수 있습니다.

 value receiver로 정의 한 경우 구조체의 데이터가 value 복사로 전달됩니다.

 pointer receiver로 정의 한 경우 구조체의 데이터가 pointer로 전달됩니다. 

 pointer receiver를 사용하는 이유는 2가지입니다.

 첫째는 메서드 내에서 전달된 값의 수정이 원본에 적용됩니다. 

 두번째는 메서드의 호출마다 value receiver는 대상 구조체의 복사를 발생됩니다. pointer receiver는 포인터만

 전달되기 때문에 크기가 큰 구조체에서는 효율적으로 동작 할 수 있습니다.

// User에 대한 구조체 정보
// User에 대한 필드 정보만으로 구성됩니다.
type User struct {
	userSN int64
	name   string
	age    int16
}

// pointer receiver
func (u *User) RefSetName(name string) {
	u.name = name
}

// value receiver
func (u User) SetName(name string) {
	u.name = name
}

// Getname 함수는 User의 name을 리턴합니다.
func (u User) Getname() string {
	return u.name
}

// PrintUser 함수는 User의 정보를 출력합니다.
func (u User) PrintUser() {
	println(u.userSN, u.name, u.age)
}

func main() {

	user1 := User{userSN: 1, name: "a", age: 20}

	// value receiver 호출
	// 내부에서 값이 변경되도 구조체에 반영되지 않음
	user1.SetName("jj")
	user1.PrintUser()

	// pointer receiver 호출
	// 내부에서 값이 변경되도 구조체에 반영되지 않음
	user1.RefSetName("jj")
	user1.PrintUser()
}

 

결과화면

 예제에서는 value receiver로 선언된 SetName함수와 pointer receiver로 선언된 RefSetName을 호출합니다.

 SetName함수를 사용하면 구조체 값에 영향을 미치지 않는 것을 확인 할 수 있습니다. 

 RefSetName 함수를 사용하면 구조체 값에 영향을 미치는 것을 확인 할 수 있습니다. 

인터페이스

 golang에서 인터페이스란 메서드들의 집합체입니다. (c++ 추상 클래스와 비슷합니다. )

 인터페이스에는 인터페이스를 사용하려는 타입들이 구현해야하는 메서드 원형을 정의 합니다. 

 구조체는 명시된 메서드를 구현하면 인터페이스 타입을 사용할 수 있습니다. 

// Character 인터페이스는 
// getSerialNumber와 getName 메서드를 구현해야 합니다.
type Character interface {
    getSerialNumber() int64
    getName() string
}

// Npc 타입은 Character 메서드를 구현합니다.
type Npc struct {
    NpcNumber int64
    Name      string
}

func (n *Npc) getSerialNumber() int64 {
    return n.NpcNumber
}

func (n *Npc) getName() string {
    return n.Name
}

// User 타입은 Character 메서드를 구현합니다.
type User struct {
    UserSerialNumber int64
    Name             string
}

func (u *User) getSerialNumber() int64 {
    return u.UserSerialNumber
}

func (u *User) getName() string {
    return u.Name
}

// Character 인터페이스 타입의 정보를 출력하는 함수입니다. 
func ShowCharacterInfo(Chars ...Character) {
    for _, c := range Chars {
        println("SerialNumber : ", c.getSerialNumber(), "\t Name : ", c.getName())
    }
}

func main() {
    npc1 := Npc{10, "물약상인"}
    npc2 := Npc{20, "무기상인"}

    user1 := User{8515147, "user1"}
    // character 인터페이스 타입 정보 출력!
    ShowCharacterInfo(&npc1, &npc2, &user1)
}

결과 화면

 예제에서는 Character 인터페이스를 통해서 getSerialNumber, getName 메서드를 명시하였고 

 User와 Npc는 해당 메서드들을 구현하였기 때문에 Character 인터페이스를 사용 할 수 있습니다.

 Main함수에서는 ShowCharacterInfo 함수를 통해서 Character 인터페이스로 변환된 npc와 user의 정보를 

 출력 할 수 있습니다.

인터페이스 타입

 golang에서 비어있는 인터페이스를 자주 보게 되는데 이것을 인터페이스 타입이라고 부릅니다.

 인터페이스 타입은 interface{}로 표현되며 Dynamic Type을 뜻합니다.(c++의 void*, c#의 object 같은)

 즉 어떤한 타입도 담을 수 있는 타입입니다. 

func main() {
    var DynamicType interface{}
    DynamicType = 1
    fmt.Println(DynamicType)
    DynamicType = 2.3
    fmt.Println(DynamicType)
    DynamicType = Npc{10, "물약상인"}
    fmt.Println(DynamicType)
}

결과 화면

'golang' 카테고리의 다른 글

[golang] Effective Go - Concurrency  (0) 2020.04.10
[golang] goroutines  (0) 2020.04.05
[golang] 익명함수, 클로져, Array, Slice, Map  (0) 2020.04.01
[golang] 변수, 상수, 키워드, 함수, 반복문  (0) 2020.03.30
[golang] Package, Import  (0) 2020.03.29

+ Recent posts