鸭子类型

概念樣例

考慮用於一個使用鴨子型別的語言的以下虛擬碼：
function calculate(a, b, c) => return (a+b)*c

example1 = calculate (1, 2, 3)
example2 = calculate ([1, 2, 3], [4, 5, 6], 2)
example3 = calculate ('apples ', 'and oranges, ', 3)

print to_string example1
print to_string example2
print to_string example3
在樣例中，每次對calculate的呼叫都使用的物件（數字、列表和字串）在繼承關係中沒有聯絡。只要物件支援「+」和「*」方法，操作就能成功。例如，翻譯成Ruby或Python語言，執行結果應該是：
9
[1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6]
apples and oranges, apples and oranges, apples and oranges, 
這樣，鴨子型別在不使用繼承的情況下使用了多型。唯一的要求是calculate函式需要作為參數的物件擁有「+」和「*」方法。以下樣例（Python語言）體現了鴨子測試。就in_the_forest函式而言，物件是一個鴨子：
 1 class Duck: 2     def quack(self): 3         print "这鸭子正在嘎嘎叫" 4  5     def feathers(self): 6         print "这鸭子拥有白色和灰色的羽毛" 7  8  9 class Person:10     def quack(self):11         print "这人正在模仿鸭子"12 13     def feathers(self):14         print "这人在地上拿起1根羽毛然后给其他人看"15 16 17 def in_the_forest(duck):18     duck.quack()19     duck.feathers()20 21 22 def game():23     donald = Duck()24     john = Person()25     in_the_forest(donald)26     in_the_forest(john)27 28 29 game()

靜態語言中的鴨子型別

一些通常的靜態語言如Boo和C#第四版，有一些額外的類型註解，它們指示編譯器將類的型別檢查安排在執行時而不是編譯時，並在編譯器的輸出中包含用於執行時型別檢查的程式碼。這些附加的內容允許這些語言享受鴨子型別的大多數益處，僅有的缺點是需要在編譯時辨識和指定這些動態類。

結構型別系統

鴨子型別和結構類型相似但與之不同。結構類型由類型的結構決定類型的相容性和等價性，而鴨子型別只由結構中在執行時所存取的部分決定類型的相容性。Objective Caml語言使用結構型別系統。

介面

介面可以提供鴨子型別的一些益處，但鴨子型別與之不同的是沒有顯式定義任何介面。例如，如果一個第三方Java庫實現了一個使用者不允許修改的類，使用者就無法把這個類別的實體用作一個自己定義的介面的實現，而鴨子型別允許這樣做。

模板或泛型

模板函式或方法在一個靜態型別上下文中應用鴨子測試；這同時帶來了靜態和動態型別檢查的一般優點和缺點。同時，由於在鴨子型別中，只有「在執行時被實際呼叫的」方法需要被實現，而模板要求實現「在編譯時不能證明不可到達的」所有方法，因此鴨子型別更具有可伸縮性。
實體包括帶有模板的C++語言和Java語言的泛型。

批評

關於鴨子型別常常被參照的一個批評是它要求程式設計師在任何時候都必須很好地理解他/她正在編寫的程式碼。在一個強靜態型別的、使用了類型繼承樹和參數型別檢查的語言中，給一個類提供未預測的物件類型更為困難。例如，在Python中，你可以建立一個稱為Wine的類，並在其中需要實現press方法。然而，一個稱為Trousers的類可能也實現press()方法。為了避免奇怪的、難以檢測的錯誤，開發者在使用鴨子型別時需要意識到每一個「press」方法的可能使用，即使在語意上和他/她所正在編寫工作的程式碼沒有任何關係。
本質上，問題是：「如果它走起來像鴨子並且叫起來像鴨子」，它也可以是一隻正在模仿鴨子的龍。儘管它們可以模仿鴨子，但也許你不總是想讓龍進入池塘。
鴨子型別的提倡者，如吉多·范羅蘇姆，認為這個問題可以通過在測試和維護程式碼庫前擁有足夠的了解來解決。
對鴨子型別的批評傾向於成為關於動態型別和靜態型別的爭論的更廣闊的觀點的特殊情形。

在ColdFusion中

web應用程式手稿語言ColdFusion允許函式參數被指定為類型為any。對於這種參數，任意物件都可被傳入，函式呼叫在執行時被動態繫結。如果物件沒有實現一個被呼叫的函式，一個可被擷取並優雅地處理的執行時異常將被丟擲。在ColdFusion 8中，這也可以被一個已定義的事件onMissingMethod()而不是例外處理器處理。另一個可替代的參數類型WEB-INF.cftags.component限制傳入參數是一個ColdFusion元件（CFC），在一個不正確的物件傳入時它提供了更好的錯誤訊息。

在C#中

C# 4.0實現了動態成員查詢（dynamic member lookup）實現了鴨子型別化。注意下例中類別方法InTheForest的參數類型被聲明為dynamic。
using System;namespace DuckTyping 
{    
    public class Duck 
    {
        public void Quack()    { Console.WriteLine("这鸭子正在嘎嘎叫"); }
        public void Feathers() { Console.WriteLine("这鸭子拥有白色与灰色羽毛"); }
    }

    public class Person 
    {
        public void Quack()    { Console.WriteLine("这人正在模仿鸭子"); }
        public void Feathers() { Console.WriteLine("这人在地上拿起1根羽毛然后给其他人看"); }
    }

    internal class Program 
    {
        private static void InTheForest(dynamic duck) 
        {
            duck.Quack();
            duck.Feathers();
        }

        private static void Game() 
        {
            Duck donald = new Duck();
            Person john = new Person();
            InTheForest(donald);
            InTheForest(john);
        }

        private static void Main() 
        {
            Game();
        }
    }}

在Common Lisp中

Common Lisp提供了一個物件導向的擴充（Common Lisp物件系統，簡寫為CLOS）。在Common Lisp中，CLOS和Lisp的動態型別使鴨子型別成為一種通用的編程風格。
使用Common Lisp，用戶通常不需要查詢類型，因為如果一個函式不適用，系統會丟擲一個執行時錯誤。這個錯誤可以被Common Lisp的條件系統處理。在類外定義的方法也可以為指定的物件定義。
(defclass duck () ())(defmethod quack ((a-duck duck))
  (print "这鸭子正在嘎嘎叫"))(defmethod feathers ((a-duck duck))
  (print "这鸭子有白色和灰色羽毛"))(defclass person () ())(defmethod quack ((a-person person))
  (print "这人正在模仿鸭子"))(defmethod feathers ((a-person person))
  (print "这人在地上拿起1根羽毛然后给其他人看"))(defmethod in-the-forest (duck)
  (quack duck)
  (feathers duck))(defmethod game ()
  (let ((donald (make-instance 'duck))
        (john (make-instance 'person)))
    (in-the-forest donald)
    (in-the-forest john)))(game)
Common Lisp通常的開發風格（像SLIME一樣使用Lisp REPL）也允許互動式修復：
? (defclass cat () ())#<STANDARD-CLASS CAT>? (quack (make-instance 'cat))> Error: There is no applicable method for the generic function:>          #<STANDARD-GENERIC-FUNCTION QUACK #x300041C2371F>>        when called with arguments:>          (#<CAT #x300041C7EEFD>)> If continued: Try calling it again1 > (defmethod quack ((a-cat cat))
        (print "这猫正在模仿鸭子"))#<STANDARD-METHOD QUACK (CAT)>1 > (continue)"这猫正在模仿鸭子"
通過這種方法，軟體可以通過擴充只有部分工作的使用鴨子型別的程式碼來開發。

在Objective-C中

Objective-C，C和Smalltalk的一個交錯，像Smalltalk一樣，允許用戶聲明一個物件的類型為「id」並向它傳送任何資訊。傳送者可以測試一個物件以了解它能不能對一個訊息回應，物件可以在收到訊息的時候決定回應與否，如果傳送者傳送了一個接收者不能回應的訊息，一個異常會被丟擲。因此，鴨子型別在Objective-C中被完全支援。

在Python中

鴨子型別在Python中被廣泛使用。Python術語表這樣定義鴨子型別：
Pythonic programming style that determines an object's type by inspection of its method or attribute signature rather than by explicit relationship to some type object ("If it looks like a duck and quacks like a duck, it must be a duck.") By emphasizing interfaces rather than specific types, well-designed code improves its flexibility by allowing polymorphic substitution. Duck-typing avoids tests using type() or isinstance(). Instead, it typically employs the EAFP (Easier to Ask Forgiveness than Permission) style of programming.
在Python中，鴨子型別的最典型例子就是類似file的類。這些類可以實現file的一些或全部方法，並可以用於file通常使用的地方。例如，GzipFile實現了一個用於存取gzip壓縮的資料的類似file的物件。cStringIO允許把一個Python字串視作一個檔案。通訊端（socket）也和檔案共同擁有許多相同的方法。然而通訊端缺少tell()方法，不能用於GzipFile可以使用的所有地方。這體現了鴨子型別的可伸縮性：一個類似file的物件可以實現它有能力實現的方法，且只能被用於它有意義的情形下。
EAFP原則描述了例外處理的使用。例如相對於檢查一個自稱為類似Duck的物件是否擁有一個quack()方法（使用if hasattr(mallard, "quack"): ...），人們通常更傾向於用例外處理把對quack的呼叫嘗試包裹起來：
try:
    mallard.quack()except (AttributeError, TypeError):
    print "mallard并沒有quack()函数"
這個寫法的優勢在於它鼓勵結構化處理其他來自類的錯誤（這樣的話，例如，一個不能完成quack的Duck子類別可以丟擲一個「QuackException」，這個異常可以簡單地添加到包裹它的程式碼，並不需要影響更多的程式碼的邏輯。同時，對於其他不同類的物件存在不相容的成員而造成的命名衝突，它也能夠處理（例如，假設有一個醫學專家Mallard有一個布林屬性將他分類為「quack=True」，試圖執行Mallard.quack()將丟擲一個TypeError）。
在更實際的實現類似file的行為的例子中，人們更傾向於使用Python的例外處理機制來處理各種各樣的可能因為各種程式設計師無法控制的環境和operating system問題而發生的I/O錯誤。在這裡，「鴨子型別」產生的異常可以在它們自己的子句中擷取，與作業系統、I/O和其他可能的錯誤分別處理，從而避開複雜的檢測和錯誤檢查邏輯。