Go 언어는 유형 어설션을 사용하여 인터페이스 유형을 수행할 수 있습니다. Go에서는 하나의 인터페이스 유형을 다른 인터페이스 유형으로 변환하든, 인터페이스를 다른 기본 유형으로 변환하든, "변환된 변수: = 인터페이스 변수. (대상 유형)"이라는 두 가지 유형의 변환 구문이 있습니다. 그리고 "변환된 변수, ok := 인터페이스 변수.(대상 유형)".
이 튜토리얼의 운영 환경: Windows 7 시스템, GO 버전 1.18, Dell G3 컴퓨터.
Golang에서 인터페이스 유형을 다른 인터페이스 유형으로 변환하거나 인터페이스를 다른 기본 유형으로 변환하려면 type 어설션을 사용해야 합니다.
타입 어설션의 형식
타입 어설션은 인터페이스 값에 사용되는 작업입니다. 구문론적으로 i.(T)는 어설션 유형이라고 불리는 것처럼 보입니다. 여기서 i는 인터페이스 유형을 나타내고 T는 유형을 나타냅니다. 유형 주장은 그것이 작동하는 객체의 동적 유형이 주장된 유형과 일치하는지 확인합니다.
유형 어설션의 기본 형식은 다음과 같습니다.
t := i.(T)
여기서 i는 인터페이스 변수를 나타내고 T는 변환된 대상 유형을 나타내며 t는 변환된 변수를 나타냅니다.
여기에는 두 가지 가능성이 있습니다. 먼저, 주장된 유형 T 가 구체적인 유형인 경우 유형 주장은 i 의 동적 유형이 T 와 동일한지 확인합니다. 이 검사가 성공하면 유형 주장의 결과는 i의 동적 값이 되며 이는 물론 T 유형입니다. 즉, 구체적인 유형에 대한 유형 주장은 피연산자로부터 구체적인 값을 얻습니다. 검사가 실패하면 후속 작업에서 패닉이 발생합니다. 예:
var w io.Writer w = os.Stdout f := w.(*os.File) // 成功: f == os.Stdout c := w.(*bytes.Buffer) // 死机:接口保存*os.file,而不是*bytes.buffer
Second, 대신 주장된 유형 T가 인터페이스 유형인 경우 유형 주장은 i의 동적 유형이 T를 충족하는지 확인합니다. 이 검사가 성공하면 동적 값이 검색되지 않습니다. 결과는 여전히 동일한 유형 및 값 부분을 가진 인터페이스 값이지만 결과에는 T 유형이 있습니다. 즉, 인터페이스 유형에 대한 유형 어설션은 유형이 표시되는 방식을 변경하고 사용 가능한 메서드 세트(일반적으로 더 큰)를 변경하지만 인터페이스 값의 동적 유형과 값 부분을 보호합니다.
아래의 첫 번째 유형 어설션 이후 w와 rw는 모두 os.Stdout을 보유하므로 각각 동적 유형 *os.File을 갖지만 변수 w는 파일의 Write 메소드만 노출하는 io.Writer 유형입니다. 그러나 rw 변수는 Read 메서드만 노출합니다.
var w io.Writer w = os.Stdout rw := w.(io.ReadWriter) // 成功:*os.file具有读写功能 w = new(ByteCounter) rw = w.(io.ReadWriter) // 死机:*字节计数器没有读取方法
어설션 작업의 개체가 nil 인터페이스 값인 경우 어설션되는 유형에 관계없이 유형 어설션이 실패합니다. nil 인터페이스 값을 제외하고 할당 작업처럼 동작하기 때문에 덜 제한적인 인터페이스 유형(더 적은 메소드 세트)에 대해 어설션할 필요가 거의 없습니다.
T 인터페이스의 메서드를 완전히 구현하지 않으면 이 문으로 인해 충돌이 발생합니다. 가동 중지 시간을 유발하는 것은 그리 친숙하지 않으므로 위 명령문을 작성하는 다른 방법이 있습니다:
t,ok := i.(T)
이런 방식으로 인터페이스가 구현되지 않으면 ok는 false로 설정되고 t는 T 유형의 0 값으로 설정됩니다. . 일반적인 구현에서는 ok가 true입니다. 여기서 ok는 인터페이스 i가 유형 T를 구현하는지 여부에 대한 결과로 생각할 수 있습니다.
인터페이스를 다른 인터페이스로 변환
특정 인터페이스를 구현하는 유형은 다른 인터페이스도 구현합니다. 이때 두 인터페이스 간에 변환할 수 있습니다.
새와 돼지는 서로 다른 특성을 가지고 있습니다. 새는 날 수 있고 돼지는 날 수 없지만 두 동물 모두 걸을 수 있습니다. 구조를 사용하여 새와 돼지를 구현하는 경우 고유한 특성을 부여하는 Fly() 및 Walk() 메서드를 사용하면 새와 돼지가 각각 나는 동물 인터페이스(Flyer)와 걷는 동물 인터페이스(Walker)를 구현할 수 있습니다.
새와 돼지의 인스턴스가 생성된 후 인터페이스{} 유형의 맵에 저장됩니다. 인터페이스{} 유형은 빈 인터페이스를 나타냅니다. 즉, 이 인터페이스는 어떤 유형으로도 저장될 수 있습니다. Bird 또는 Pig의 인스턴스를 보유하는 인터페이스{} 변수에 대해 어설션 작업을 수행합니다. 어설션 개체가 어설션에 지정된 유형인 경우 지정된 어설션 유형이 아닌 경우 어설션 개체 유형으로 변환된 인터페이스가 반환됩니다. , 어설션의 두 번째 매개변수는 false를 반환합니다.
예를 들어 다음 코드는
var obj interface = new(bird) f, isFlyer := obj.(Flyer)
코드에서 new(bird)는 *bird 유형의 새 인스턴스를 생성하고 이 인스턴스는 인터페이스{} 유형의 obj 변수에 저장됩니다. obj.(Flyer) 유형 어설션을 사용하여 obj를 Flyer 인터페이스로 변환합니다. f는 변환 성공 시 Flyer 인터페이스 유형이고, isFlyer는 변환 성공 여부를 나타내며 유형은 bool입니다.
상세 코드(코드 1)는 다음과 같습니다.
package main import "fmt" // 定义飞行动物接口 type Flyer interface { Fly() } // 定义行走动物接口 type Walker interface { Walk() } // 定义鸟类 type bird struct { } // 实现飞行动物接口 func (b *bird) Fly() { fmt.Println("bird: fly") } // 为鸟添加Walk()方法, 实现行走动物接口 func (b *bird) Walk() { fmt.Println("bird: walk") } // 定义猪 type pig struct { } // 为猪添加Walk()方法, 实现行走动物接口 func (p *pig) Walk() { fmt.Println("pig: walk") } func main() { // 创建动物的名字到实例的映射 animals := map[string]interface{}{ "bird": new(bird), "pig": new(pig), } // 遍历映射 for name, obj := range animals { // 判断对象是否为飞行动物 f, isFlyer := obj.(Flyer) // 判断对象是否为行走动物 w, isWalker := obj.(Walker) fmt.Printf("name: %s isFlyer: %v isWalker: %v\n", name, isFlyer, isWalker) // 如果是飞行动物则调用飞行动物接口 if isFlyer { f.Fly() } // 如果是行走动物则调用行走动物接口 if isWalker { w.Walk() } } }
코드 설명은 다음과 같습니다.
6번째 줄은 날아다니는 동물의 인터페이스를 정의합니다.
라인 11은 걷는 동물 인터페이스를 정의합니다.
16행과 30행은 각각 새와 돼지 객체를 정의하고 각각 날아다니는 동물과 걷는 동물 인터페이스를 구현합니다.
라인 41은 개체 이름과 개체 인스턴스를 매핑하는 맵입니다. 인스턴스는 새와 돼지입니다.
라인 47은 지도 탐색을 시작합니다. obj는 인터페이스{} 인터페이스 유형입니다.
第 50 行中,使用类型断言获得 f,类型为 Flyer 及 isFlyer 的断言成功的判定。
第 52 行中,使用类型断言获得 w,类型为 Walker 及 isWalker 的断言成功的判定。
第 57 和 62 行,根据飞行动物和行走动物两者是否断言成功,调用其接口。
代码输出如下:
将接口转换为其他类型
在代码 1 中,可以实现将接口转换为普通的指针类型。例如将 Walker 接口转换为 *pig 类型,请参考下面的代码:
p1 := new(pig) var a Walker = p1 p2 := a.(*pig) fmt.Printf("p1=%p p2=%p", p1, p2)
对代码的说明如下:
第 3 行,由于 pig 实现了 Walker 接口,因此可以被隐式转换为 Walker 接口类型保存于 a 中。
第 4 行,由于 a 中保存的本来就是 *pig 本体,因此可以转换为 *pig 类型。
第 6 行,对比发现,p1 和 p2 指针是相同的。
如果尝试将上面这段代码中的 Walker 类型的 a 转换为 *bird 类型,将会发出运行时错误,请参考下面的代码:
p1 := new(pig) var a Walker = p1 p2 := a.(*bird)
运行时报错:
panic: interface conversion: main.Walker is *main.pig, not *main.bird
报错意思是:接口转换时,main.Walker 接口的内部保存的是 *main.pig,而不是 *main.bird。
因此,接口在转换为其他类型时,接口内保存的实例对应的类型指针,必须是要转换的对应的类型指针。
总结
接口和其他类型的转换可以在Go语言中自由进行,前提是已经完全实现。
接口断言类似于流程控制中的 if。但大量类型断言出现时,应使用更为高效的类型分支 switch 特性。
위 내용은 Go 언어 인터페이스 유형을 변환하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!