Javaなどの静的に型付けされた言語は、型のコンパイル時チェックの利点を提供します-オブジェクトが特定の型であることが保証されているので、次のようになります。
None
、List[string]
、または1つだけのstring
であるかどうかわからないため、変数またはパラメーターのTYPEを調査するために時間とリソースを費やす必要はありません。intPhoneNumber
を文字列に変更しましたが、名前をstrPhoneNumber
に変更するのを忘れました)私が言及していない他の点がおそらくあります。しかし、一般的な考え方は、プログラマーの認知的負荷が少なく、誤った仮定がある場合、フィードバックはコンパイルの失敗という形で即座に提供されるというものです。そして、即時フィードバックはまさに単体テストの勝利要因の1つです。
一方、ダックタイピングはオブジェクトの明示的なタイプを気にしません-オブジェクトが関連する属性(データメンバーと関数)を持っている限り、プログラムは先に進むことができます。オブジェクトがコードの一部で必要とされるものと同じ名前の関数を持っている場合、実装が予期せず異なっていても、コードはその関数を使用しようとします。オブジェクトに必要な属性がない場合、プログラムはクラッシュします。これの利点は、コードが削減されることです。ダックタイピング言語ではインターフェイス定義は必要ありません。
上記のように、このような予期しないシナリオは実行時にのみキャッチできます。しかし、静的に型付けされた言語は、コンパイル時にそのような不一致をキャッチします。堅牢な単体テストスイートが100%pathコードカバレッジでセットアップされていない限り、そのような基本的な-そして予防可能な-エラー(タイプの不一致)は見逃されがちです。また、パスコードカバレッジはもちろんのこと、コードカバレッジのこのような高い割合を達成することは、さまざまな理由で非常に難しいことを私たちは知っています。
したがって:
一方、ダックタイピングはオブジェクトの明示的なタイプを気にしません-オブジェクトが関連する属性(データメンバーと関数)を持っている限り、プログラムは先に進むことができます。オブジェクトがコードの一部で必要とされるものと同じ名前の関数を持っている場合、実装が予期せず異なっていても、コードはその関数を使用しようとします。
IFooインターフェイスを作成すると、それから派生し、すべてのメソッドの実装が完全に間違っているBadFooクラスを作成できます。これにより、作成したコード内で、適切に作成されたIFooインスタンスに依存するあらゆる種類のランタイムエラーが発生します。コンパイラは私がそれをするのを止めることはできません。結果として、この主張は:
堅牢な単体テストスイートが100%のパスコードカバレッジでセットアップされていない限り、このような基本的な(そして予防可能な)エラー(タイプの不一致)は見逃されがちです。
all言語では、静的または動的であるかどうか、および何らかの形式のダックタイピングをサポートしているかどうかにかかわらず、絶対に当てはまります。私の経験では、従来の静的に型付けされた言語で発生する型関連のランタイムエラーは、それらの言語が生成する可能性のある最も陰湿なエラーの1つです。
原則として、コンパイラはIFooがメソッドfoo()を含むインターフェイスであり、MegaCorpFooがfoo()メソッドを持つクラスであることを簡単に認識できるため、MegaCorpFooはIFooインターフェイスを満たしていると結論付けますプログラマが明示的に行う必要はありません)そう言う。
私の知る限り、これを明確に行う言語はGoだけです。 C++テンプレートは間違いなくそれを実現しますが、脆弱で不可解な方法です。 JavaまたはC#でリフレクションAPIを使用して、これに相当するものをハックすることもできます。
C++、JavaおよびC#はすべて、さまざまな形式のキャストを許可します。これらの多くは、ランタイムエラーにつながる可能性があるという意味で、本質的に「安全ではありません」。
静的型付け(従来のOOP言語)とダックタイピング)の実際の違いは、ダックタイピングの安全性が低いということではなく、MyFooが明示的に宣言 IFooインターフェースを満たし、IFooインターフェースが必要です明示的に定義されています最初のどこかに。
したがって、実際のトレードオフは必ずしも安全性の1つではなく、単に冗長性の1つです。
この冗長性の利点は、コンパイラーが私からより詳細な命令を受け取るため、コンパイル時にさらに多くのことをチェックできることです。これにより、メモリリークや競合状態、および自分では発見できない可能性のあるその他の微妙な問題を見つけることができる、より強力な静的分析ツールも有効になる傾向があります。
欠点は、これらのより詳細な命令が、実際に気になるロジックを実装する「実際のコード」の邪魔になることです。これにより、理解が少し難しくなり、後で変更する必要があるときに壊れやすくなります。より厳密な型システムは、サードパーティクラスの周りにラッパークラスを記述して、インターフェイスを満たす宣言を追加する必要がある場合など、役立つのと同じように邪魔になることもできます。第三者はそれを知る方法がありませんでした。
私の個人的な意見では、非常に頻繁に変更する必要があり、多くの依存関係があるアプリケーション(多くのWebアプリのように)は、変更を行うプログラマーが「実際のコード」を実行することに専念できる、冗長性の低い言語の方が適していることがよくあります。コードを再度変更する必要が生じる前に発生する可能性が低いコーナーケースで型システムと戦う代わりに、正しいことと一連のテストに合格します。一方、変更されることはめったになく、依存関係がほとんどなく、長期間にわたって非常に信頼性と一貫性が必要なアプリケーション(データベースやWebサーバーなど)は、時間をかけてすべてを明示的に定義することでより多くのメリットが得られます。タイプを取得し、コンパイル時のチェックと静的分析コンパイラを増やすことで、多くのクラスのバグがおそらく不可能であることを確認します。
理想的には、最終的には、デフォルトとして静的型付けと、ダックタイピングなどの冗長性を低減する機能を組み合わせて、両方の世界を最大限に活用し、この議論全体が問題にならない言語を考案する予定です。
あなたはそこに間違った仮定を持っています。ダックタイピングcan静的にチェックされます。コンパイラーは、関数に渡される型と、その関数に必要な操作を認識しているため、不一致がある場合にエラーを検出できます。
同様に、2番目の弾丸は少し楽観的です。 Javaのような言語には型キャストがあります。つまり、それらをサポートしていないもので操作を実行しようとすると、canランタイムエラーが発生します。
とはいえ、型注釈のない言語の主張は、読みやすいということです。コードを乱雑にする型注釈の「ノイズ」がないため、実際に何が起こっているかを読み取ることができます。同様に、動的言語の場合、あなたはとにかくユニットテストを行う予定ですであるという議論がなされているので、それは過度の負担ではありません。
個人的に、私はそれらの議論に同意しませんが、私のニーズは他の人とは異なります。