一般来说不会。
您可以使用技巧(如其他答案中所建议的那样)来完成这项工作,但它们并不能提供与 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(); // results in a SomeNegatableClass, not an X
}
这对于这个应用程序来说并不是特别严重,但确实会给其他 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 代码的人感到困惑。