В компьютерном программировании утиная печать — это применение теста на уток — «Если оно ходит как утка и крякает как утка, то это, должно быть, утка» — чтобы определить, можно ли использовать объект для определенной цели. При именительной типизации объект относится к заданному типу, если он объявлен как таковой (или если связь типа с объектом выводится с помощью таких механизмов, как наследование объекта ). При утиной типизации объект относится к заданному типу, если он имеет все методы и свойства, необходимые для этого типа. [1] [2] Утиную типизацию можно рассматривать как основанную на использовании структурную эквивалентность между данным объектом и требованиями типа.
Этот простой пример в Python 3 демонстрирует, как любой объект может использоваться в любом контексте, пока он не будет использован способом, который он не поддерживает.
класс Утка : защита плавания ( сам ): распечатать ( «Утка плавает» ) защита летать ( сам ): распечатать ( «Утка летит» )класс Кит : защита плавания ( сам ): распечатать ( «Плавание китов» )для животного в [ Утка (), Кит ()]: животное . плавать () животное . летать ()
Выход:
Утка плавает Утка летает Плавает кит AttributeError: объект «Кит» не имеет атрибута «летать»
Если предположить, что все, что умеет плавать, является уткой, поскольку утки умеют плавать, то кита можно считать уткой; однако, если также предположить, что утка должна быть способна летать, кит не будет считаться уткой.
В некоторых статически типизированных языках, таких как Boo [3] и D , [4] [5] проверка типа класса может выполняться во время выполнения, а не во время компиляции.
Утиная типизация похожа на структурную типизацию , но отличается от нее . Структурная типизация — это статическая система типизации , которая определяет совместимость и эквивалентность типов по структуре типа, тогда как утиная типизация является динамической и определяет совместимость типов только по той части структуры типа, к которой осуществляется доступ во время выполнения .
Языки TypeScript , [ 6] Elm [7] и Python [8] в разной степени поддерживают структурную типизацию.
Протоколы и интерфейсы предоставляют возможность явно объявить, что некоторые методы, операторы или поведения должны быть определены. Если сторонняя библиотека реализует класс, который нельзя изменить, клиент не может использовать его экземпляр с интерфейсом, неизвестным этой библиотеке, даже если класс удовлетворяет требованиям к интерфейсу. Распространенным решением этой проблемы является шаблон адаптера . Напротив, при утином наборе объект будет принят напрямую, без необходимости использования адаптера.
Шаблонные (также называемые универсальными ) функции или методы применяют утиный тест в контексте статической типизации ; это раскрывает все преимущества и недостатки статической и динамической проверки типов . Утиная типизация также может быть более гибкой, поскольку должны быть реализованы только те методы, которые действительно вызываются во время выполнения, в то время как шаблоны требуют реализации всех методов, недостижимость которых не может быть доказана во время компиляции.
В таких языках, как Java, Scala и Objective-C, отражение может использоваться для проверки того, реализуют ли объекты методы или добавляют ли необходимые методы во время выполнения. Например, таким образом можно использовать Java MethodHandle API. [9]