вторник, 29 января 2019 г.

Эффективный Go: проверка интерфейса

Как мы обсуждали в постах ранее, тип не должен явно объявлять, что он реализует интерфейс. Вместо этого тип реализует интерфейс просто путем реализации методов интерфейса. На практике большинство преобразований интерфейса являются статическими и поэтому проверяются во время компиляции. Например, передача функции *os.File в функцию, ожидающую io.Reader, не скомпилируется, если *os.File не реализует интерфейс io.Reader.

Однако некоторые проверки интерфейса происходят во время выполнения. Один экземпляр находится в encoding/json пакете, который определяет Marshaler интерфейс. Когда кодировщик JSON получает значение, которое реализует этот интерфейс, кодировщик вызывает метод маршалинга значения, чтобы преобразовать его в JSON вместо того, чтобы делать стандартное преобразование. Кодировщик проверяет это свойство во время выполнения с утверждением типа (type assertion), например:

m, ok := val.(json.Marshaler)

Если нужно только спросить, реализует ли тип интерфейс, без применения интерфейса на самом деле (возможно, как часть проверки ошибок), используйте пустой идентификатор для игнорирования значения проверяемого типа:

if _, ok := val.(json.Marshaler); ok {
    fmt.Printf(
     "value %v of type %T implements json.Marshaler\n",
      val, val)
}

Единственное место, где возникает такая ситуация - это когда необходимо гарантировать в пакете, реализующем тип, что пакет фактически удовлетворяет интерфейсу. Если тип, например, json.RawMessage - тогда необходимо пользовательское представление JSON, оно должно реализовывать json.Marshaler, но нет статических преобразований, которые бы заставили компилятор проверить это автоматически. Если тип случайно не удовлетворяет интерфейсу, кодировщик JSON все еще будет работать, но не будет использовать пользовательскую реализацию. Чтобы гарантировать правильность реализации, глобальное объявление с использованием пустого идентификатора может быть использовано в пакете:

var _ json.Marshaler = (*RawMessage)(nil)

В этой декларации, назначение, включающее преобразование *RawMessage в Marshaler, требует, чтобы *RawMessage реализовал Marshaler, и это свойство будет проверено во время компиляции. Если интерфейс json.Marshaler изменится, этот пакет больше не будет компилироваться, и мы будем предупреждены, что его нужно обновить.

Появление пустого идентификатора в этой конструкции означает, что объявление существует только для проверки типа, не создавая переменную. Не делайте этого для каждого типа, который удовлетворяет интерфейсу. По договоренности, такие декларации используются только когда в коде уже нет статических преобразований, что является редким случаем.


Читайте также:


Комментариев нет:

Отправить комментарий