Bagaimana cara menentukan tipe "nyata" dari nilai {} antarmuka?

120

Saya belum menemukan sumber yang bagus untuk menggunakan interface{}tipe. Sebagai contoh

package main

import "fmt"

func weirdFunc(i int) interface{} {
    if i == 0 {
        return "zero"
    }
    return i
}
func main() {
    var i = 5
    var w = weirdFunc(5)

    // this example works!
    if tmp, ok := w.(int); ok {
        i += tmp
    }

    fmt.Println("i =", i)
}

Apakah Anda mengetahui pengenalan yang baik untuk menggunakan Go interface{}?

pertanyaan spesifik:

  • bagaimana cara mendapatkan Tipe w yang "asli"?
  • apakah ada cara untuk mendapatkan representasi string dari suatu tipe?
  • apakah ada cara untuk menggunakan representasi string dari suatu tipe untuk mengonversi nilai?
cc muda
sumber

Jawaban:

98

Teladan Anda berhasil. Ini versi yang disederhanakan.

package main

import "fmt"

func weird(i int) interface{} {
    if i < 0 {
        return "negative"
    }
    return i
}

func main() {
    var i = 42
    if w, ok := weird(7).(int); ok {
        i += w
    }
    if w, ok := weird(-100).(int); ok {
        i += w
    }
    fmt.Println("i =", i)
}

Output:
i = 49

Ini menggunakan pernyataan Jenis .

peterSO
sumber
Anda memang benar! Terima kasih! apakah Anda memiliki wawasan tentang jenis representasi string jenis?
cc muda
12
Lihat reflect.TypeOf.
Dmitri Goldring
@DmitriGoldring Setidaknya menjawab pertanyaan di judul topik. Jawaban ini tidak. Terima kasih banyak.
C4d
130

Anda juga dapat melakukan sakelar tipe:

switch v := myInterface.(type) {
case int:
    // v is an int here, so e.g. v + 1 is possible.
    fmt.Printf("Integer: %v", v)
case float64:
    // v is a float64 here, so e.g. v + 1.0 is possible.
    fmt.Printf("Float64: %v", v)
case string:
    // v is a string here, so e.g. v + " Yeah!" is possible.
    fmt.Printf("String: %v", v)
default:
    // And here I'm feeling dumb. ;)
    fmt.Printf("I don't know, ask stackoverflow.")
}
themue
sumber
Terima kasih untuk itu. tapi masih belum cukup. dalam contoh, bagaimana cara memaksa var w menjadi int?
cc muda
3
Contoh Mue melakukan hal yang sama, tetapi dalam sakelar tipe, bukan pernyataan if. Dalam 'case int', 'v' akan menjadi integer. dalam 'case float64', 'v' akan menjadi float64, dll.
jimt
Baik. telah melupakan sintaks var. (tipe), yang licik dan keren
cc muda
51

Anda dapat menggunakan refleksi ( reflect.TypeOf()) untuk mendapatkan jenis sesuatu, dan nilai yang diberikannya ( Type) memiliki representasi string ( Stringmetode) yang dapat Anda cetak.

newacct
sumber
10
Dan jika Anda hanya ingin mendapatkan string atau tipe (mis. Untuk mencetak di blok default dari tautan sakelar tipe dalam jawaban Mue, Anda bisa menggunakan fmtformat "% T" daripada langsung menggunakan reflect.
Dave C
16

Berikut adalah contoh decoding peta generik menggunakan switch dan refleksi, jadi jika Anda tidak cocok dengan tipenya, gunakan refleksi untuk mencari tahu dan kemudian tambahkan tipe di lain waktu.

var data map[string]interface {}

...

for k, v := range data {
    fmt.Printf("pair:%s\t%s\n", k, v)   

    switch t := v.(type) {
    case int:
        fmt.Printf("Integer: %v\n", t)
    case float64:
        fmt.Printf("Float64: %v\n", t)
    case string:
        fmt.Printf("String: %v\n", t)
    case bool:
        fmt.Printf("Bool: %v\n", t)
    case []interface {}:
        for i,n := range t {
            fmt.Printf("Item: %v= %v\n", i, n)
        }
    default:
        var r = reflect.TypeOf(t)
        fmt.Printf("Other:%v\n", r)             
    }
}
h4ck3rm1k3
sumber
6

Sakelar tipe juga dapat digunakan dengan benda refleksi:

var str = "hello!"
var obj = reflect.ValueOf(&str)

switch obj.Elem().Interface().(type) {
case string:
    log.Println("obj contains a pointer to a string")
default:
    log.Println("obj contains something else")
}
Nikolai Koudelia
sumber
2

Saya akan menawarkan cara untuk mengembalikan boolean berdasarkan menyampaikan argumen Jenis refleksi ke penerima tipe lokal (karena saya tidak dapat menemukan yang seperti ini).

Pertama, kami mendeklarasikan tipe anonim kami dari tipe reflect. Nilai:

type AnonymousType reflect.Value

Kemudian kami menambahkan pembangun untuk tipe lokal AnonymousType kami yang dapat menerima tipe potensial apa pun (sebagai antarmuka):

func ToAnonymousType(obj interface{}) AnonymousType {
    return AnonymousType(reflect.ValueOf(obj))
}

Kemudian kami menambahkan fungsi untuk struct AnonymousType kami yang menegaskan terhadap sebuah reflect.

func (a AnonymousType) IsA(typeToAssert reflect.Kind) bool {
    return typeToAssert == reflect.Value(a).Kind()
}

Ini memungkinkan kami untuk memanggil yang berikut:

var f float64 = 3.4

anon := ToAnonymousType(f)

if anon.IsA(reflect.String) {
    fmt.Println("Its A String!")
} else if anon.IsA(reflect.Float32) {
    fmt.Println("Its A Float32!")
} else if anon.IsA(reflect.Float64) {
    fmt.Println("Its A Float64!")
} else {
    fmt.Println("Failed")
}

Dapat melihat versi yang lebih lama dan berfungsi di sini: https://play.golang.org/p/EIAp0z62B7

daino3
sumber
1

Ada beberapa cara untuk mendapatkan representasi string dari suatu tipe. Sakelar juga dapat digunakan dengan jenis pengguna:

var user interface{}
user = User{name: "Eugene"}

// .(type) can only be used inside a switch
switch v := user.(type) {
case int:
    // Built-in types are possible (int, float64, string, etc.)
    fmt.Printf("Integer: %v", v)
case User:
    // User defined types work as well  
    fmt.Printf("It's a user: %s\n", user.(User).name)
}

// You can use reflection to get *reflect.rtype
userType := reflect.TypeOf(user)
fmt.Printf("%+v\n", userType)

// You can also use %T to get a string value
fmt.Printf("%T", user)

// You can even get it into a string
userTypeAsString := fmt.Sprintf("%T", user)

if userTypeAsString == "main.User" {
    fmt.Printf("\nIt's definitely a user")
}

Tautan ke taman bermain: https://play.golang.org/p/VDeNDUd9uK6

Eugene Kulabuhov
sumber