Como determinar o {} tipo “real” de um valor de interface?

120

Não encontrei um bom recurso para usar interface{}tipos. Por exemplo

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)
}

Você conhece uma boa introdução ao uso do Go interface{}?

questões específicas:

  • como obtenho o tipo "real" de w?
  • existe alguma maneira de obter a representação de string de um tipo?
  • existe alguma maneira de usar a representação de string de um tipo para converter um valor?
cc jovem
fonte

Respostas:

98

Seu exemplo funciona. Aqui está uma versão simplificada.

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

Ele usa asserções de tipo .

PeterSO
fonte
você está absolutamente correto! obrigado! você tem alguma ideia sobre as representações de strings de tipos?
cc jovem de
12
Confira reflect.TypeOf.
Dmitri Goldring
@DmitriGoldring Isso pelo menos responde à pergunta no título dos tópicos. Esta resposta não. Muito obrigado.
C4d
129

Você também pode fazer mudanças de tipo:

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.")
}
tema
fonte
obrigado por isso. mas ainda não está lá. no exemplo, como faço para coagir var w em um int?
cc jovem de
3
O exemplo de Mue faz a mesma coisa, mas em uma troca de tipo em vez de uma instrução if. No 'caso int', 'v' será um inteiro. no 'caso float64', 'v' será um float64, etc.
jimt
certo. tinha esquecido a sintaxe var. (tipo), que é sorrateira e legal
cc jovem de
51

Você pode usar reflexão ( reflect.TypeOf()) para obter o tipo de algo, e o valor que ele fornece ( Type) tem uma representação de string ( Stringmétodo) que você pode imprimir.

newacct
fonte
10
E se você quiser apenas obter uma string ou um tipo (por exemplo, para imprimir no bloco padrão de um link de mudança de tipo na resposta de Mue, você pode apenas usar fmto formato "% T" em vez de usar diretamente reflect.
Dave C
16

Aqui está um exemplo de decodificação de um mapa genérico usando switch e reflexão, então se você não combinar o tipo, use reflexão para descobrir e então adicione o tipo na próxima vez.

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
fonte
6

Os interruptores de tipo também podem ser usados ​​com material de reflexão:

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
fonte
2

Vou oferecer uma maneira de retornar um booleano com base na passagem de um argumento de um tipo de reflexão para um receptor de tipo local (porque não consegui encontrar nada parecido com isso).

Primeiro, declaramos nosso tipo anônimo de tipo reflect.Value:

type AnonymousType reflect.Value

Em seguida, adicionamos um construtor para nosso tipo local AnonymousType que pode receber qualquer tipo potencial (como uma interface):

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

Em seguida, adicionamos uma função para nossa estrutura AnonymousType que atua contra um reflect.Kind:

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

Isso nos permite chamar o seguinte:

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")
}

Pode ver uma versão mais longa e funcional aqui: https://play.golang.org/p/EIAp0z62B7

daino3
fonte
1

Existem várias maneiras de obter uma representação de string de um tipo. Os switches também podem ser usados ​​com tipos de usuário:

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")
}

Link para um playground: https://play.golang.org/p/VDeNDUd9uK6

Eugene Kulabuhov
fonte