Groovy duck类型与静态类型相比有哪些优点?

Groovy duck类型与静态类型相比有哪些优点?,groovy,duck-typing,Groovy,Duck Typing,我正在用Groovy进行更多的研究和实验,我正试着用Groovy实现Java中不能/不可以实现的东西的利弊。动态编程对我来说仍然只是一个概念,因为我对静态和强类型语言有着深刻的了解 Groovy给了我这样的能力,但我看不出它的价值。duck类型如何比静态类型更高效?在我的代码实践中,我可以做什么来帮助我掌握它的好处 我问这个问题的时候考虑到了Groovy,但我明白这不一定是Groovy的问题,所以我欢迎来自每个代码阵营的答案。不是duck类型比静态类型更高效,而是它与静态类型不同。使用静态类型时

我正在用Groovy进行更多的研究和实验,我正试着用Groovy实现Java中不能/不可以实现的东西的利弊。动态编程对我来说仍然只是一个概念,因为我对静态和强类型语言有着深刻的了解

Groovy给了我这样的能力,但我看不出它的价值。duck类型如何比静态类型更高效?在我的代码实践中,我可以做什么来帮助我掌握它的好处


我问这个问题的时候考虑到了Groovy,但我明白这不一定是Groovy的问题,所以我欢迎来自每个代码阵营的答案。

不是duck类型比静态类型更高效,而是它与静态类型不同。使用静态类型时,您必须始终担心数据的类型是否正确,而在Java中,它是通过转换到正确的类型来显示的。对于duck类型,只要它有正确的方法,类型就无关紧要,因此它实际上只是消除了大量类型转换和转换的麻烦。

在使用duck类型一段时间之前,很难看到它的价值。一旦你习惯了它,你就会意识到不必处理接口或者不必担心什么类型的东西是什么,这会减轻你的负担。

如果你使用Haskell,静态类型没有什么问题,它有一个令人难以置信的静态类型系统。但是,如果使用java和C++语言,这些语言有非常糟糕的类型,那么鸭子类型的输入无疑是一种改进。
想象一下,在Java中尝试使用像“”这样简单的东西(不,我不是指)。即使是泛型也没有得到很好的支持。

Duck类型削弱了大多数现代IDE的静态检查,它可以在您键入时指出错误。有些人认为这是一种优势。我希望IDE/编译器尽快告诉我,我做了一个愚蠢的程序员把戏

我最近最喜欢的反对duck类型的论点来自Grails项目DTO:

class SimpleResults {
    def results
    def total
    def categories
}
其中,
results
类似于
Map
,只有通过跟踪不同类中的方法调用,直到找到它的创建位置,才能发现它。对于极端好奇的人,
total
列表
s大小的总和,
类别
地图


最初的开发人员可能已经很清楚了,但是可怜的维护人员(我)在跟踪这一点时失去了很多头发。

嗯,如果你坚持一些约定,比如以一致的方式命名变量和方法,duck类型的优势就会被放大。以Ken G为例,我认为最好是:

class SimpleResults {
    def mapOfListResults
    def total
    def categories
}
假设您在一个名为“计算(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);
使用duck类型,您可以放弃接口,只需依赖于运行时,A和B都将正确响应您的
doStuff()
调用。不需要具体的合同定义。这可能对你有用,但也可能对你不利

缺点是,您必须格外小心,以确保当其他人更改代码时,代码不会中断(即,其他人必须知道方法名称和参数的隐式约定)


注意,这种情况在Java中尤其严重,因为Java中的语法并不像它可能的那样简洁(例如,与Java相比)。一个反例是,他们说框架的SLOC计数与类似,但测试代码的行数较少,因为他们不需要在测试中实现类型检查。

接下来,哪个更好:EMACS还是vi?这是一场正在进行的宗教战争

这样想:如果语言是静态类型的,那么任何正确的程序都是正确的。静态类型的作用是让编译器有足够的信息在编译时而不是运行时检测类型不匹配。如果你在做增量编程,这可能是一件麻烦事,尽管(我坚持)如果你清楚地思考你的程序,这没什么大不了的;另一方面,如果你正在构建一个非常大的程序,比如操作系统或电话交换机,有几十人、数百人或数千人在上面工作,或者有非常高的可靠性要求,然后让编译器能够为您检测一大类问题,而不需要测试用例来执行正确的代码路径

这并不是说动态类型是一种新的、不同的东西:例如,C是有效的动态类型,因为我总是可以将
foo*
转换为
bar*
。这只是意味着,作为一名C程序员,当地址真正指向
foo*
时,我的责任就是永远不要在
条*
上使用合适的代码。但是,由于大型程序的问题,C增加了诸如LIT(1)这样的工具,用<代码> TyPulfF加强了它的类型系统,并最终在C++中开发了一个强类型的变体。当然,C++也在强类型的周围发展了各种类型的类型、模板/模板和RTTI。 不过,还有一件事——不要把“敏捷编程”和“动态语言”混为一谈。这是关于人们在项目中共同工作的方式:项目能否适应不断变化的需求,以满足客户的需求,同时保持一个良好的工作环境
class BookFinder {
    def searchEngine

    def findBookByTitle(String title) {
         return searchEngine.find( [ "Title" : title ] ) 
    }
}
void bookFinderTest() {
    // with Expando we can 'fake' any object at runtime.
    // alternatively you could write a MockSearchEngine class.
    def mockSearchEngine = new Expando()
    mockSearchEngine.find = {
        return new Book("Heart of Darkness","Joseph Conrad")
    }

    def bf = new BookFinder()
    bf.searchEngine = mockSearchEngine
    def book = bf.findBookByTitle("Heart of Darkness")
    assert(book.author == "Joseph Conrad"
}
//Static typing 
Map<String,List<Class1<Class2>>> someMap = [:] as HashMap<String,List<Class1<Class2>>>
//Dynamic typing
def someMap = [:]