一般来说,不行。
你可以使用一些技巧(如其他答案中建议的),使其工作,但它们不能提供Haskell类型类所提供的所有保证。具体来说,在Haskell中,我可以定义这样一个函数:
doublyNegate :: Negatable t => t -> t
doublyNegate v = negate (negate v)
现在已知
doublyNegate
的参数和返回值都是
t
。但 Java 中的相应函数为:
public <T extends Negatable<T>> T doublyNegate (Negatable<T> v)
{
return v.negate().negate();
}
不会,因为Negatable<T>
可以由另一种类型实现:
public class X implements Negatable<SomeNegatableClass> {
public SomeNegatableClass negate () { return new SomeNegatableClass(); }
public static void main (String[] args) {
new X().negate().negate();
}
对于这个应用程序来说并不是特别严重,但它会给其他Haskell类型类带来麻烦,例如 Equatable
。没有办法实现Java Equatable
类型类而不使用额外的对象,并将该对象的实例发送到需要比较值的任何地方(例如:
public interface Equatable<T> {
boolean equal (T a, T b);
}
public class MyClass
{
String str;
public static class MyClassEquatable implements Equatable<MyClass>
{
public boolean equal (MyClass a, MyClass b) {
return a.str.equals(b.str);
}
}
}
...
public <T> methodThatNeedsToEquateThings (T a, T b, Equatable<T> eq)
{
if (eq.equal (a, b)) { System.out.println ("they're equal!"); }
}
实际上,这正是Haskell实现类型类的方式,但它会将参数传递隐藏起来,因此您不需要弄清楚应该将哪个实现发送到哪里。
仅使用普通Java接口尝试执行此操作会导致一些反直觉的结果:
public interface Equatable<T extends Equatable<T>>
{
boolean equalTo (T other);
}
public MyClass implements Equatable<MyClass>
{
String str;
public boolean equalTo (MyClass other)
{
return str.equals(other.str);
}
}
public Another implements Equatable<MyClass>
{
public boolean equalTo (MyClass other)
{
return true;
}
}
....
MyClass a = ....;
Another b = ....;
if (b.equalTo(a))
assertTrue (a.equalTo(b));
....
由于
equalTo
应该被对称地定义,因此您预期如果那里的
if
语句编译,则断言也会编译,但实际上并没有,因为
MyClass
与
Another
不可比较,即使相反情况是正确的。 但是使用Haskell的
Equatable
类型类,我们知道如果
areEqual a b
有效,则
areEqual b a
也是有效的。 [1]
接口与类型类的另一个限制是,类型类可以提供创建实现类型类的值的方法,而无需存在现有值(例如
Monad
的
return
运算符),而对于接口,必须已经拥有该类型的对象才能调用其方法。
您问是否有这种限制的名称,但我不知道。这只是因为尽管它们具有相似之处,但类型类实际上与面向对象接口不同,因为它们以根本性不同的方式实现:对象是其接口的子类型,因此直接携带接口的方法副本而不修改其定义,而类型类是一系列函数列表,每个函数都通过替换类型变量进行自定义。类型和具有该类型实例的类型类之间不存在子类型关系(例如,Haskell的
Integer
不是
Comparable
的子类型,只是存在一个
Comparable
实例,每当函数需要能够比较其参数并且这些参数恰好是整数时,该实例就可以传递)。
[1]:Haskell的
==
运算符实际上使用类型类
Eq
实现...我没有使用它,因为Haskell中的运算符重载可能会使不熟悉阅读Haskell代码的人感到困惑。
public interface Negatable<T extends Negatable>
,这样无论如何都可以链接否定操作。或者有什么原因不这样做吗? - kutschkemv :: Negatable t => t
,那么v
和negate v
都是类型为t
的值,而在Java中,您可以拥有Negatable <T> v
但实际上没有v
是类型t
(例如,我可以定义public class X implements Negatable<Integer> { public void negate() {return 1; }}
,它满足所有给定条件,但由于不符合类型类中指定的正确反射性,Haskell将禁止此操作。 - Jules