interface {}의 의미는 무엇입니까?


133

인터페이스를 처음 사용하고 github의 SOAP 요청을하려고합니다.

나는의 의미를 이해하지 못한다

Msg interface{}

이 코드에서 :

type Envelope struct {
    Body `xml:"soap:"`
}

type Body struct {
    Msg interface{}
}

나는 같은 구문을 관찰했다.

fmt.Println

그러나 무엇이 달성되고 있는지 이해하지 못한다

interface{}

20
interface{}void *C 와 동등하다
Nick Craig-Wood

interface {}의 의미는 무엇입니까? stackoverflow.com/a/62337836/12817546을 참조하십시오 .
Tom J

답변:


189

" Gos 에서 인터페이스를 사용하는 방법 "( " Russ Cox의 인터페이스 설명 "을 기반으로) 기사를 참조 할 수 있습니다 .

무엇 이며 인터페이스는?

인터페이스는 두 가지입니다.

  • 일련의 방법이며
  • 그러나 그것은 또한 유형입니다

interface{}유형은 빈 인터페이스는 아무런 방법이없는 인터페이스입니다.

구현 키워드가 없기 때문에 모든 유형은 최소 0 개의 메소드를 구현하며 인터페이스 만족은 자동으로 수행 되므로 모든 유형은 빈 인터페이스를 충족합니다 .
즉, interface{}값을 매개 변수로 사용 하는 함수를 작성하면 해당 함수에 임의의 값을 제공 할 수 있습니다 .

(그것은 Msg당신의 질문에서 대표하는 것입니다 : 모든 가치)

func DoSomething(v interface{}) {
   // ...
}

혼란스러운 부분은 다음과 같습니다.

DoSomething함수 내부에서 유형 은 무엇 v입니까?

초보자 고퍼들은“ v모든 유형의” 것이라고 믿게 되지만 그것은 잘못입니다.
v어떤 유형도 아닙니다. 그것은이다 interface{}유형 .

에 값을 전달할 때 DoSomething기능의 이동 런타임에서 수행 타입 변환 (필요할 경우), 및 내지 An 값을 변환 interface{} .
모든 값은 런타임시 정확히 하나의 유형을 가지며, v하나의 정적 유형은 interface{}입니다.

인터페이스 값은 두 단어의 데이터로 구성됩니다 .

  • 한 단어는 값의 기본 유형에 대한 메소드 테이블을 가리키는 데 사용됩니다.
  • 다른 단어는 해당 값이 보유한 실제 데이터를 가리키는 데 사용됩니다.

부록 : 인터페이스 구조에 관한 Russ의 기사는 다음과 같습니다.

type Stringer interface {
    String() string
}

인터페이스 값은 인터페이스에 저장된 유형에 대한 정보를 가리키는 포인터와 관련 데이터를 가리키는 두 단어 쌍으로 표시됩니다.
Stringer 유형의 인터페이스 값에 b를 지정하면 인터페이스 값의 두 단어가 설정됩니다.

http://research.swtch.com/gointer2.png

인터페이스 값의 첫 번째 단어는 내가 인터페이스 테이블 또는 itable이라고 부르는 것을 가리 킵니다 (i-table로 발음됩니다. 런타임 소스에서 C 구현 이름은 Itab입니다).
itable은 관련된 유형에 대한 일부 메타 데이터로 시작한 다음 함수 포인터 목록이됩니다.
itable은 dynamic type이 아닌 인터페이스 유형에 해당합니다 .
이 예에서는 StringerBinary 유형 을 보유 할 수있는 Stringer를 만족시키는 데 사용되는 메소드를 나열합니다 String. Binary의 다른 메소드 ( Get)는에 나타나지 않습니다 itable.

인터페이스 값의 두 번째 단어는 실제 데이터 ( 이 경우 사본)를 가리 킵니다b .
할당은 var s Stringer = b(A)의 복사본을 만드는 b에서보다는 포인트 b같은 이유로 var c uint64 = b복사본을 만듭니다를 다음과 같은 경우 b나중에 변경, s그리고 c원래 값이 아닌 새로운 하나가 가정된다.
인터페이스에 저장된 값은 임의로 클 수 있지만 인터페이스 구조에서 값을 보유하기 위해 한 단어 만 사용되므로 할당은 힙에 메모리 청크를 할당하고 포인터를 한 단어 슬롯에 기록합니다.


4
"두 단어의 데이터"란 무엇을 의미합니까? 구체적으로, "단어"는 무엇을 의미합니까?
Mingyu

3
@Mingyu 나는이 두 단어 (32 비트 포인트)를 설명하는 답을 완성했습니다.
VonC

2
@Mingyu : VonC는 참조되는 단어 컴퓨터 아키텍처 의미 - 데이터의 고정 된 크기의 부분을 정의하는 비트의 컬렉션. 단어 크기는 사용중인 프로세서 아키텍처에 의해 결정됩니다.
Dan Esparza

1
귀하의 답변에 대한 @VonC 감사합니다 ... 사실은 내가 물건을 요청할 때 내리막 길에 지쳐 있다는 것입니다. 사람들은 대부분 내가 문서를 읽어야한다고 말합니다 ... 그것에 대한 게시물을 제대로 쓸 것입니다 ...하지만 다른 방법으로 물어볼 수는 없습니다. 어쨌든 고마워하고 내 낮은 의지를 용서하십시오. 왜 물어 보지 않으려는지 명확히하기 위해 stackoverflow.com/questions/45577301/… 을 살펴 보십시오.
Victor

1
@vic no problem, asker로서의 이전의 나쁜 경험으로 죄송합니다. 의견은 질문과 답변에 적합하지 않습니다.
VonC

34

interface{}사용자 정의 유형을 포함하여 모든 유형의 값을 넣을 수 있음을 의미합니다. Go의 모든 유형은 빈 인터페이스를 충족합니다 ( interface{}빈 인터페이스 임).
귀하의 예에서 메시지 필드는 모든 유형의 값을 가질 수 있습니다.

예:

package main

import (
    "fmt"
)

type Body struct {
    Msg interface{}
}

func main() {
    b := Body{}
    b.Msg = "5"
    fmt.Printf("%#v %T \n", b.Msg, b.Msg) // Output: "5" string
    b.Msg = 5

    fmt.Printf("%#v %T", b.Msg, b.Msg) //Output:  5 int
}

운동장 이동


12

빈 인터페이스 라고하며 모든 유형으로 구현되므로 Msg필드 에 무엇이든 넣을 수 있습니다 .

예 :

body := Body{3}
fmt.Printf("%#v\n", body) // -> main.Body{Msg:3}

body = Body{"anything"}
fmt.Printf("%#v\n", body) // -> main.Body{Msg:"anything"}

body = Body{body}
fmt.Printf("%#v\n", body) // -> main.Body{Msg:main.Body{Msg:"anything"}}

이것은 유형이 인터페이스의 모든 메소드를 갖 자마자 인터페이스를 구현한다는 사실의 논리적 확장입니다.


사용자 정의 구조에 대한 int 일 수 있음을 의미합니까 ??
사용자

11

이미 좋은 답변이 있습니다. 직관적으로 이해하려는 다른 사람들을 위해 내 자신을 추가하겠습니다.


상호 작용

하나의 메소드가있는 인터페이스는 다음과 같습니다.

type Runner interface {
    Run()
}

따라서 Run()메소드 가있는 모든 유형 은 Runner 인터페이스를 충족시킵니다.

type Program struct {
    /* fields */
}

func (p Program) Run() {
    /* running */
}

func (p Program) Stop() {
    /* stopping */
}
  • 프로그램 유형에도 Stop 메소드가 있지만 필요한 모든 인터페이스 메소드를 갖기 만하면되기 때문에 Runner 인터페이스를 여전히 만족시킵니다.

  • 따라서 Run 메소드가 있으며 Runner 인터페이스를 충족시킵니다.


빈 인터페이스

메소드가없는 명명 된 빈 인터페이스는 다음과 같습니다.

type Empty interface {
    /* it has no methods */
}

따라서 모든 유형이이 인터페이스를 충족합니다. 이 인터페이스를 만족시키기위한 방법이 필요하지 않기 때문입니다. 예를 들면 다음과 같습니다.

// Because, Empty interface has no methods, following types satisfy the Empty interface
var a Empty

a = 5
a = 6.5
a = "hello"

그러나 위의 프로그램 유형이이를 만족합니까? 예:

a = Program{} // ok

interface {}는 위의 빈 인터페이스와 같습니다.

var b interface{}

// true: a == b

b = a
b = 9
b = "bye"

보시다시피, 신비한 것은 없지만 남용하기는 매우 쉽습니다. 최대한 멀리 떨어져 있으십시오.


https://play.golang.org/p/A-vwTddWJ7G


type Runner interface이동 놀이터 예에서는 사용되지 않습니다.
Tom J

9

로부터 Golang 사양 :

인터페이스 유형은 해당 인터페이스라고하는 메소드 세트를 지정합니다. 인터페이스 유형의 변수는 인터페이스의 상위 집합 인 메소드 세트를 사용하여 모든 유형의 값을 저장할 수 있습니다. 이러한 유형은 인터페이스를 구현한다고합니다. 초기화되지 않은 인터페이스 유형의 변수 값은 nil입니다.

유형은 메소드의 서브 세트를 포함하는 인터페이스를 구현하므로 여러 가지 고유 한 인터페이스를 구현할 수 있습니다. 예를 들어, 모든 유형은 빈 인터페이스를 구현합니다.

상호 작용{}

파악해야 할 개념은 다음과 같습니다.

  1. 모든 유형이 있습니다. 당신의 지금 T.하자 말을 부르 자, 새로운 유형을 정의 할 수 있습니다 우리의 유형은 T3 가지 방법이 있습니다 A, B, C.
  2. 유형에 지정된 메소드 세트를 " 인터페이스 유형 " 이라고합니다 . 우리의 예에서 T_interface라고 부르겠습니다. 동일하다T_interface = (A, B, C)
  3. 메소드 의 서명 을 정의하여 "인터페이스 유형"을 작성할 수 있습니다 .MyInterface = (A, )
  4. "interface type" 유형변수 를 지정 하면 인터페이스의 상위 집합 인 인터페이스가있는 유형 만 변수 에 지정할 수 있습니다. 즉, 포함 된 모든 메소드 가 내부에 포함되어야합니다.MyInterfaceT_interface

모든 유형의 모든 "인터페이스 유형"이 빈 인터페이스의 수퍼 세트라고 추론 할 수 있습니다.


1

@VonC의 탁월한 답변과 @ NickCraig-Wood의 주석을 확장하는 예제입니다. interface{}무엇이든 가리킬 수 있으며 사용하려면 캐스트 / 유형 어설 션이 필요합니다.

package main

import (
    . "fmt"
    "strconv"
)

var c = cat("Fish")
var d = dog("Bone")

func main() {
    var i interface{} = c
    switch i.(type) {
    case cat:
        c.Eat() // Fish
    }

    i = d
    switch i.(type) {
    case dog:
        d.Eat() // Bone
    }

    i = "4.3"
    Printf("%T %v\n", i, i) // string 4.3
    s, _ := i.(string)      // type assertion
    f, _ := strconv.ParseFloat(s, 64)
    n := int(f)             // type conversion
    Printf("%T %v\n", n, n) // int 4
}

type cat string
type dog string
func (c cat) Eat() { Println(c) }
func (d dog) Eat() { Println(d) }

i값이있는 빈 인터페이스의 변수입니다 cat("Fish"). 인터페이스 유형의 값에서 메소드 값을 작성하는 것이 합법적입니다. https://golang.org/ref/spec#Interface_types를 참조 하십시오 .

타입 스위치 i는 인터페이스 타입이 임을 확인 cat("Fish")합니다. https://golang.org/doc/effective_go.html#type_switch를 참조 하십시오 . i그런 다음에 다시 할당됩니다 dog("Bone"). 유형 스위치는 i인터페이스 유형이로 변경 되었음을 확인합니다 dog("Bone").

또한 할당을 시도 하여 유형 T이 인터페이스 I를 구현 하는지 확인하도록 컴파일러에 요청할 수 있습니다 var _ I = T{}. 참조 https://golang.org/doc/faq#guarantee_satisfies_interfacehttps://stackoverflow.com/a/60663003/12817546 .

모든 유형은 빈 인터페이스를 구현합니다 interface{}. 참조 https://talks.golang.org/2012/goforc.slide#44https://golang.org/ref/spec#Interface_types . 이 예에서는 이번에 i는 문자열 "4.3"에 다시 할당됩니다. i다음 새로운 문자열 변수에 할당 s하여 i.(string)전에 sfloat64 형식으로 변환되어 f사용 strconv. 마지막으로 4와 같은 int 형식 으로 f변환됩니다 n. 형식 변환과 형식 어설 션의 차이점무엇입니까?를 참조하십시오 .

Go의 내장 된 맵과 슬라이스와 빈 인터페이스를 사용하여 컨테이너를 생성하는 기능 (명시 적 언 박싱 포함)은 일반적으로 덜 매끄럽다면 제네릭이 가능하게하는 코드를 작성할 수 있다는 것을 의미합니다. https://golang.org/doc/faq#generics를 참조 하십시오 .


인터페이스와 코드를 분리합니다. stackoverflow.com/a/62297796/12817546을 참조하십시오 . "동적으로"메소드를 호출하십시오. stackoverflow.com/a/62336440/12817546을 참조하십시오 . Go 패키지에 액세스하십시오. stackoverflow.com/a/62278078/12817546을 참조하십시오 . 변수에 값을 지정하십시오. stackoverflow.com/a/62337836/12817546을 참조하십시오 .
Tom J
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.