问这个问题是为了澄清我对类型类和更高级类型的理解,我并不是在Java中寻找解决方法。
在 haskell ,我可以这样写
class Negatable t where
negate :: t -> t
normalize :: (Negatable t) => t -> t
normalize x = negate (negate x)
假设Bool
有一个Negatable
的示例,
v :: Bool
v = normalize True
一切都很好。
在Java中,似乎不可能声明一个合适的Negatable
接口,我们可以写:
interface Negatable {
Negatable negate();
}
Negatable normalize(Negatable a) {
a.negate().negate();
}
但是,与Haskell中不同的是,如果不进行强制转换,以下代码将无法编译(假设MyBoolean
实现Negatable
):
MyBoolean val = normalize(new MyBoolean()); // does not compile; val is a Negatable, not a MyBoolean
- 是否有一种方法可以在Java接口中引用实现类型,或者这是Java类型系统的一个基本限制?**如果是限制,是否与更高级类型的支持有关?我认为没有:看起来这是另一种限制。2如果是这样,它有名字吗?
谢谢,如果问题不清楚请告诉我!
4条答案
按热度按时间2uluyalo1#
实际上,是的。不是直接地,但是你可以这样做。简单地包括一个泛型参数,然后从泛型类型派生。
您可以这样实现此接口
实际上,Java标准库正是使用这种技巧来实现
Comparable
。fquxozlt2#
一般来说,没有。
你可以使用一些技巧(如其他答案中所建议的)来实现这个功能,但是它们不能提供Haskell类型类所提供的所有保证,具体来说,在Haskell中,我可以定义一个如下的函数:
现在我们知道
doublyNegate
的参数和返回值都是t
,但是Java的等价物是:不会,因为
Negatable<T>
可以由另一种类型实现:这对于这个应用程序来说并不是特别严重,但是对于其他Haskell类型类,例如
Equatable
,确实会造成麻烦。如果不使用一个额外的对象,并在我们发送需要比较的值的任何地方发送该对象的示例,就没有办法实现JavaEquatable
类型类,(例如:(In事实上,这正是Haskell实现类型类的方式,但它隐藏了传递给您的参数,因此您不需要弄清楚将哪个实现发送到哪里)
尝试仅使用普通的Java接口来实现这一点会导致一些违反直觉的结果:
由于
equalTo
确实应该被对称定义,你可能会认为如果if
语句被编译了,Assert也会被编译,但是它没有,因为MyClass
不等同于Another
,尽管相反的情况是正确的,但是对于HaskellEquatable
类型类,我们知道如果areEqual a b
工作,则areEqual b a
也有效。[1]接口相对于类型类的另一个限制是,类型类可以提供一种方法来创建一个值,该值在没有现有值的情况下实现类型类(例如,
Monad
的return
运算符),而对于接口,您必须已经具有该类型的对象才能调用其方法。你问这个限制有没有名字,但我不知道,这只是因为类型类实际上不同于面向对象的接口,尽管它们有相似之处,因为它们是以这种根本不同的方式实现的:一个对象是它的接口的一个子类型,因此直接携带接口的方法的副本,而不修改它们的定义,而一个类型类是一个单独的函数列表,每个函数都是通过替换类型变量定制的。在类型和具有该类型示例的类型类之间没有子类型关系(Haskell
Integer
不是Comparable
的子类型,例如:简单地存在Comparable
示例,每当函数需要能够比较其参数并且那些参数碰巧是整数时,可以传递该示例)。[1]:Haskell
==
运算符实际上是使用类型类Eq
实现的......我没有使用它,因为Haskell中的运算符重载可能会让不熟悉阅读Haskell代码的人感到困惑。uyto3xhc3#
我对这个问题的理解是,
我们如何使用Java中的类型类实现ad-hoc多态性?
你可以在Java中做一些非常类似的事情,但是没有Haskell的类型安全保证--下面给出的解决方案可能会在运行时抛出错误。
下面是您可以如何做到这一点:
1.定义表示类型类的接口
1.实现一些机制,允许你为各种类型 * 注册类型类的示例 *。这里,静态的
HashMap
可以做到:1.定义
normalize
方法,该方法使用上述机制基于传递对象的运行时类获取适当的示例:1.注册各种类的实际示例:
1.用它!
主要的缺点是,正如已经提到的,类型类示例查找可能在运行时失败,而不是在编译时使用静态散列Map也不是最佳的,因为它带来了共享全局变量的所有问题,这可以通过更复杂的依赖注入机制来减轻。从其他类型类示例自动生成类型类示例,可能需要更多的基础设施(可以在库中完成),但原则上,它使用Java中的类型类实现了ad-hoc多态性。
完整代码:
slwdgvem4#
你要寻找的是泛型加上自类型。自类型是泛型占位符的概念,它等同于示例的类。
然而,java中不存在自类型。
您可以接近泛型,但它很笨拙:
然后
对实施者的一些影响:
MyBoolean implements Negatable<MyBoolean>
MyBoolean
需要再次覆盖negate
方法。