Убедитесь, что тип реализует интерфейс во время компиляции в Go

Как я могу гарантировать, что тип реализует интерфейс во время компиляции? Типичный способ сделать это - не назначить поддерживающие интерфейсы этого типа, однако у меня есть несколько типов, которые преобразуются только динамически. Во время выполнения это генерирует очень грубые сообщения об ошибках, без лучшей диагностики для ошибок времени компиляции. Также очень неудобно находить во время выполнения, что типы, которые я ожидал поддерживать интерфейсы, на самом деле не делают.

5 ответов

Решение

Предполагая, что вопрос о Go, например

var _ foo.RequiredInterface = myType{} // or &myType{} or [&]myType if scalar

как TLD проверит это для вас во время компиляции.

РЕДАКТИРОВАТЬ: s / [*] / & /

EDIT2: s / dummy / _ /, спасибо Atom

В языке Go нет декларации "реализует" по замыслу. Единственный способ попросить компилятор проверить, что тип T реализует интерфейс I, путем попытки присвоения (да, фиктивного:). Обратите внимание, что Go lang различает методы, объявленные для структуры и указателя, используйте правильный в проверке присваивания!

type T struct{}
var _ I = T{}       // Verify that T implements I.
var _ I = (*T)(nil) // Verify that *T implements I.

Подробности читайте в FAQ. Почему у Go нет объявлений "реализует"?

Расширение ответа с помощью @smile-on.

В Как я могу гарантировать, что мой тип удовлетворяет интерфейсу?, который является частью Часто задаваемых вопросов (FAQ) авторов Go, говорится следующее:

Вы можете попросить компилятор проверить, что тип T реализует интерфейс I путем попытки присвоения с использованием нулевого значения для T или указатель на T, в зависимости от обстоятельств.

Мы можем проиллюстрировать это на примере:

package main

type I interface{ M() }
type T struct{}

func (T) M() {}

//func (*T) M() {} //var _ I = T{}: T does not implement I (M method has pointer receiver)

func main() {
  //avoids allocation of memory
  var _ I = T{}       // Verify that T implements I.
  var _ I = (*T)(nil) // Verify that *T implements I.
  //allocation of memory
  var _ I = &T{}      // Verify that &T implements I.
  var _ I = new(T)    // Verify that new(T) implements I.
}

Если T (или *Tсоответственно) не реализует I, ошибка будет обнаружена во время компиляции. См. Неинтерфейсные методы в реализации интерфейса.

Обычно вы проверяете, реализует ли значение интерфейс, если не знаете его тип. Если он известен, проверка выполняется компилятором автоматически. См. Объяснение проверки, реализует ли значение интерфейс.

Пустой идентификатор _ обозначает имя переменной, которое здесь не нужно (и, таким образом, предотвращает ошибку "объявлено, но не используется"). (*T)(nil) создает неинициализированный указатель на значение типа T преобразовав nil к *T. См. У вас проблемы с пониманием части кода golang.

Это то же значение, которое, например, var t *Tимеет, прежде чем что-либо ему присвоить. См. Проверку типа компиляции на соответствие интерфейсу golang. Это позволяет избежать выделения памяти для пустой структуры, как если бы вы получили&T{} или new(T). См. У вас проблемы с пониманием части кода golang.

Цитаты отредактированы для соответствия примеру.

Как это:

http://play.golang.org/p/57Vq0z1hq0

package main

import(
    "fmt"
)

type Test int

func(t *Test) SayHello() {
    fmt.Println("Hello");   
}

type Saluter interface{
    SayHello()
    SayBye()
}

func main() {
    t := Saluter(new(Test))
    t.SayHello()
}

Даст:

prog.go:19: cannot convert new(Test) (type *Test) to type Saluter:
    *Test does not implement Saluter (missing SayBye method)
package main

import (
    "fmt"
)

type Sayer interface {
    Say()
}

type Person struct {
    Name string
}

func(this *Person) Say() {
    fmt.Println("I am", this.Name)
}

func main() {
    person := &Person{"polaris"}

    Test(person)
}

func Test(i interface{}) {
    //!!here ,judge i implement Sayer
    if sayer, ok := i.(Sayer); ok {
        sayer.Say()
    }
}

Пример кода здесь: http://play.golang.org/p/22bgbYVV6q

Мне не нравится идея заставлять компилятор выдавать ошибки, вставляя в основной код фиктивные строки. Это умное решение, которое работает, но я предпочитаю написать тест для этой цели.

Предполагая, что у нас есть:

type Intfc interface { Func() }
type Typ int
func (t Typ) Func() {}

Этот тест проверяет Typ инвентарь Intfc:

package main

import (
    "reflect"
    "testing"
)

func TestTypes(t *testing.T) {
    var interfaces struct {
        intfc Intfc
    }
    var typ Typ
    v := reflect.ValueOf(interfaces)
    testType(t, reflect.TypeOf(typ), v.Field(0).Type())
}

// testType checks if type t1 implements interface t2
func testType(t *testing.T, t1, t2 reflect.Type) {
    if !t1.Implements(t2) {
        t.Errorf("%v does not implement %v", t1, t2)
    }
}

Вы можете проверить все свои типы и интерфейсы, добавив их в TestTypes функция. Написание тестов для Go введено здесь.

Другие вопросы по тегам