Java的接口和Haskell的类型类:差异和相似之处?

zw3*_*324 100 java haskell interface typeclass

在我学习Haskell的过程中,我注意到它的类型类,它应该是一个源自Haskell的伟大发明.

但是,在类型类的维基百科页面中:

程序员通过指定一组函数或常量名称以及它们各自的类型来定义类型类,这些类型必须存在于属于该类的每个类型.

这似乎与Java的界面非常接近(引用维基百科的界面(Java)页面):

Java编程语言中的接口是一种抽象类型,用于指定类必须实现的接口(在术语的一般意义上).

这两个看起来很相似:类型类限制了类型的行为,而接口限制了类的行为.

我想知道Haskell中的类类和Java中的接口之间有什么区别和相似之处,或者它们可能根本不同?

编辑:我注意到即使haskell.org也承认它们是相似的.如果它们如此相似(或者它们是什么?),那么为什么类型类会受到这样的炒作?

更多编辑:哇,这么多很棒的答案!我想我必须让社区决定哪个是最好的.然而,在阅读答案时,所有人似乎都只是说"在界面无法或不得不应对泛型时,类节目可以做很多事情".我不禁想知道,有什么接口可以做而类型不能吗?此外,我注意到维基百科声称类型类最初是在1989年的论文中发明的*"如何使ad-hoc多态性不那么特别",而Haskell仍处于摇篮之中,而Java项目始于1991年并于1995年首次发布那么也许代码类似于接口,而另一种方式,接口受类型类的影响?是否有任何文件/文件支持或反驳?感谢所有答案,他们都非常有启发性!

感谢所有的投入!

new*_*cct 45

我会说接口有点像类型类SomeInterface t,其中所有值都具有类型t -> whatever(其中whatever不包含t).这是因为Java和类似语言中的继承关系类型,调用的方法取决于它们被调用的对象的类型,而不是其他.

这意味着很难add :: t -> t -> t用接口来创建多个参数的多态,因为接口无法指定方法的参数类型和返回类型与类型的类型相同.它被调用的对象(即"自我"类型).使用Generics,有一种方法可以通过创建一个泛型参数的接口来伪造它,这个参数应该与对象本身的类型相同,比如它Comparable<T>是什么样的,你应该使用Foo implements Comparable<Foo>哪种compareTo(T otherobject)类型的类型t -> t -> Ordering.但是,这仍然需要程序员遵循这个规则,并且当人们想要创建使用该接口的函数时也会引起麻烦,他们必须具有递归泛型类型参数.

此外,你不会有empty :: t因为你没有在这里调用函数,所以它不是一种方法.

  • Scala 特征(基本上是接口)允许 this.type,因此您可以返回或接受“self 类型”的参数。Scala 有一个完全独立的特性,他们称之为“自类型”顺便说一句,与此无关。这些都不是概念上的差异,只是实现上的差异。 (2认同)

Dan*_*att 42

接口和类型类之间的相似之处在于它们命名并描述了一组相关的操作.操作本身通过其名称,输入和输出来描述.同样地,这些操作的许多实现可能在它们的实现方面不同.

除此之外,这里有一些值得注意的差异:

  • 接口方法始终与对象实例相关联.换句话说,总是存在隐含的"this"参数,该参数是调用该方法的对象.类型类函数的所有输入都是显式的.
  • 必须将接口实现定义为实现接口的类的一部分.相反,类型类"实例"可以完全独立于其关联类型...即使在另一个模块中也是如此.
  • 类型类允许您为任何已定义的操作定义"默认"实现.接口只是严格的类型规范,没有实现.

总的来说,我认为可以说类型类比接口更强大和灵活.您如何定义用于将字符串转换为实现类型的某个值或实例的接口?这当然不是不可能的,但结果不会直观或优雅.您是否曾希望在某些已编译的库中实现类型的接口?使用类型类很容易实现这些.

  • 根据Java 8在接口中的默认实现,可能值得更新此答案. (9认同)
  • @CMCDragonkai 是的,您可以例如说“class (Foo a) =&gt; Bar a where...”来指定 Bar 类型类扩展 Foo 类型类。与 Java 一样,Haskell 在这里也有多重继承。 (2认同)

C. *_*ann 22

类型类被创建为表达"ad-hoc多态"的结构化方式,这基本上是重载函数的技术术语.类型类定义如下所示:

class Foobar a where
    foo :: a -> a -> Bool
    bar :: String -> a
Run Code Online (Sandbox Code Playgroud)

这意味着,当您使用将函数foo应用于属于该类的某些类型的参数时Foobar,它会查找foo特定于该类型的实现,并使用它.这与C语言中运算符重载的情况非常相似,除了更灵活和通用之外.

接口在OO语言中起到类似的作用,但基本概念有所不同; OO语言带有Haskell根本没有的类型层次结构的内置概念,这在某些方面使问题复杂化,因为接口可能涉及通过子类型重载(即,在适当的实例上调用方法,实现其超类型所做的接口的子类型)并且通过基于平面类型的调度(因为实现接口的两个类可能没有也实现它的公共超类).鉴于子类型引入了巨大的额外复杂性,我建议将类型类视为非OO语言中重载函数的改进版本会更有帮助.

另外值得注意的是,类型类具有更灵活的调度方式 - 接口通常仅适用于实现它的单个类,而类型类是为类型定义的,类型可以出现在类函数的签名中的任何位置.OO接口中的等价物将允许接口定义将该类的对象传递给其他类的方法,定义静态方法和构造函数,这些方法将根据调用上下文中所需的返回类型选择实现,定义方法获取与实现接口的类相同的参数,以及其他根本没有真正转换的内容.

简而言之:它们用于类似的目的,但它们的工作方式有所不同,类型类更具表现力,在某些情况下,由于处理固定类型而不是继承层次结构,因此更易于使用.


cla*_*lay 14

我已经阅读了上述答案.我觉得我可以稍微清楚地回答一下:

Haskell"类型类"和Java/C#"接口"或Scala"特征"基本上是类似的.它们之间没有概念上的区别,但存在实现差异:

  • Haskell类型类使用与数据类型定义分开的"实例"实现.在C#/ Java/Scala中,接口/特征必须在类定义中实现.
  • Haskell类型类允许您返回此类型或自我类型.Scala特征也可以(this.type).请注意,Scala中的"自我类型"是一个完全不相关的功能.Java/C#需要使用泛型来处理这种行为.
  • Haskell类型类允许您定义函数(包括常量)而无需输入"this"类型参数.Java/C#接口和Scala特征在所有函数上都需要"this"输入参数.
  • Haskell类型类允许您定义函数的默认实现.Scala特性和Java 8+接口也是如此.C#可以使用扩展方法来近似这样的东西.

  • 只是为了给这个答案点添加一些文献,我阅读了 [On (Haskell) Type Classes and (C#) Interfaces](http://joeduffyblog.com/2010/02/28/on-haskell-type-classes-and- c-interfaces),它也与 C# 接口而不是 Javas 进行比较,但应该很好地在概念方面理解跨语言边界的接口概念。 (2认同)

ewe*_*nli 10

Master的编程思想中,有一个关于Haskell和类型类的发明者Phil Wadler的访谈,他解释了Java中的接口和Haskell中的类型类之间的相似之处:

一种Java方法,如:

   public static <T extends Comparable<T>> T min (T x, T y) 
   {
      if (x.compare(y) < 0)
            return x; 
      else
            return y; 
   }
Run Code Online (Sandbox Code Playgroud)

与Haskell方法非常相似:

   min :: Ord a => a -> a -> a
   min x y  = if x < y then x else y
Run Code Online (Sandbox Code Playgroud)

因此,类型类与接口有关,但实际对应性是使用上述类型参数化的静态方法.

  • 应该是这个答案,因为根据[Phil Wadler](http://homepages.inf.ed.ac.uk/wadler/)的主页,他是Haskell的原理设计者,同时他还设计了Java 的泛型扩展,后来被包含在该语言本身中。 (2认同)

mca*_*dre 9

观看Phillip Wadler的讲话,信仰,进化和编程语言.Wadler曾在Haskell工作,是Java Generics的主要贡献者.


Cha*_*ton 8

阅读软件扩展和与类类的集成,其中给出了类型类如何解决接口无法解决的许多问题的示例.

本文中列出的示例包括:

  • 表达问题,
  • 框架集成问题,
  • 独立可扩展性的问题,
  • 主导分裂,散射和纠缠的暴政.

  • 链接已经死了.试试[这一个](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.99.8567&rep=rep1&type=pdf). (3认同)
  • 好吧,现在那个人也死了。尝试[这个](http://www.informatik.uni-marburg.de/~kos/papers/gpce06.pdf) (2认同)

Ale*_* C. 6

它们是相似的(阅读:具有相似的用法),并且可能以类似的方式实现:Haskell中的多态函数在幕后采用了“ vtable”,其中列出了与类型类相关的函数。

通常可以在编译时推导出该表。在Java中,这可能不太正确。

但这是功能表,而不是方法表。方法绑定到对象,而Haskell类型类则不绑定。

看到它们就像Java的泛型一样。


Chr*_*ris 6

我不能说"炒作"级别,如果看起来那么好.但是类型类在很多方面都是相似的.我能想到的一个区别是,你可以为Haskell提供某些类型类操作的行为:

class  Eq a  where
  (==), (/=) :: a -> a -> Bool
  x /= y     = not (x == y)
  x == y     = not (x /= y)
Run Code Online (Sandbox Code Playgroud)

这表明对于作为类类实例的事物,有两个操作,相等(==)和不相等.但是,不等的操作是用等于(因此你只需提供一个)来定义的,反之亦然.(/=)Eq

所以在可能不合法的Java中可能是这样的:

interface Equal<T> {
    bool isEqual(T other) {
        return !isNotEqual(other); 
    }

    bool isNotEqual(T other) {
        return !isEqual(other); 
    }
}
Run Code Online (Sandbox Code Playgroud)

它的工作方式是你只需要提供其中一种方法来实现接口.所以我要说的是,在接口级别提供所需行为的部分实现的能力是不同的.