ИМХО, преимущество утиной типизации возрастает, когда вы придерживаетесь некоторых соглашений, таких как согласование имен переменных и методов. Если взять пример из Ken G , я думаю, что это будет лучше всего читать:
class SimpleResults {
def mapOfListResults
def total
def categories
}
Допустим, вы определили контракт для некоторой операции с именем «CalcuRating (A, B)», где A и B придерживаются другого контракта. В псевдокоде это будет выглядеть так:
Long calculateRating(A someObj, B, otherObj) {
//some fake algorithm here:
if(someObj.doStuff('foo') > otherObj.doStuff('bar')) return someObj.calcRating());
else return otherObj.calcRating();
}
Если вы хотите реализовать это в Java, и A, и B должны реализовать какой-то интерфейс, который выглядит примерно так:
public interface MyService {
public int doStuff(String input);
}
Кроме того, если вы хотите обобщить ваш контракт для расчета рейтинга (скажем, у вас есть другой алгоритм для расчета рейтинга), вам также необходимо создать интерфейс:
public long calculateRating(MyService A, MyServiceB);
С помощью утки вы можете отказаться от своих интерфейсов и просто полагать, что во время выполнения и A, и B будут правильно отвечать на ваши doStuff()
вызовы. Нет необходимости в конкретном определении контракта. Это может сработать для вас, но может и против вас.
Недостатком является то, что вы должны быть особенно осторожны, чтобы гарантировать, что ваш код не сломается, когда некоторые другие люди его изменяют (т. Е. Другой человек должен знать о неявном контракте на имя метода и аргументы).
Обратите внимание, что это особенно ухудшается в Java, где синтаксис не такой краткий, как мог бы быть (по сравнению, например, с Scala ). Контрпримером этого является Lift framework , где говорят, что SLOC count фреймворка похож на Rails , но в тестовом коде меньше строк, потому что они этого не делают необходимо реализовать проверки типов в тестах.