java - 什么时候需要动态多态性(与静态多态性相比)?
问题描述
我来自函数式语言(例如 Haskell),我非常喜欢使用类型类来实现多态性,这也是实现临时重载的一种结构性方法。
但是,最近我开始了解OOP对实际问题建模的方式,我很好奇为什么我们需要OOP 语言(例如 Java)中的动态多态性。根据我的经验,大多数函数调用都可以在编译时解决,因为许多函数式语言不支持子类型化。
所以我的问题是,在什么样的情况下我们必须使用动态多态而不是编译时多态?我的猜测是:
- 当我们使用有对象的子类型系统时,我们无法确定它的实际类型(例如,我们有一个包含许多不同类型对象的容器。但是,在这种情况下,为什么不尝试使用代数数据类型或联合类型来对容器的元素类型进行建模?)。
- 我们只有对象,我们不知道它的方法的真实名称,所以我们必须使用vptr表来帮助我们。
解决方案
在 Haskell 中,我们将“动态多态性”替换为高阶函数。
考虑以下问题:我们要定义一个表示谓词的类型。当我们实现列表时,我们最终将使用这种类型的 Predicate,以便我们可以定义过滤器函数。我们希望能够轻松定义相等谓词、小于谓词,并能够通过用“and”连接两个谓词来组合它们。
一个合理的 Java 尝试应该是这样的。
interface Predicate<T> {
public abstract boolean predicate(T x);
}
class EqualsPredicate<T> implements Predicate<T> {
private final T value;
public EqualsPredicate(T value) {
this.value = value;
}
@Override
public boolean predicate(T x) {
return this.value.equals(x);
}
}
class LessPredicate<T implements Comparable<T>> implements Predicate<T>{
private final T value;
public LessPredicate(T value) {
this.value = value;
}
@Override
public boolean predicate(T x) {
return this.value.compareTo(x) < 0;
}
}
class AndPredicate<T> implements Predicate<T> {
private final Predicate<T> p1;
private final Predicate<T> p2;
public AndPredicate(Predicate<T> p1, Predicate<T> p2) {
this.p1 = p1;
this.p2 = p2;
}
@Override
public boolean predicate(T x) {
return p1.predicate(x) && p2.predicate(x);
}
}
在 Haskell 中,这个难题的答案是显而易见的。我们只定义
type Predicate t = t -> Bool
makeEqualsPredicate :: Eq t => t -> Predicate t
makeEqualsPredicate = (==)
makeLessPredicate :: Ord t => t -> Predicate t
makeLessPredicate = (<)
makeAndPredicate :: Predicate t -> Predicate t -> Predicate t
makeAndPredicate p1 p2 x = p1 x && p2 x
-- or, even more succinctly, makeAndPredicate = liftA2 (&&)
Java 允许通过子类化“动态分派”方法。Haskell 允许通过高阶函数“动态调度”函数。
但是等等,你说。Predicate 是一个只有一种方法的接口。如果我们想要有两种方法,我们应该怎么做?
那么,如果一个接口有一个方法对应一个函数,那么一个接口有两个方法一定对应一对函数。这就是所谓的“组合优于继承”的 OOP 原则。
所以我们总是可以用 Haskell 风格的高阶函数代替 Java 风格的动态多态性。
事实上,您实际上在现代 Java 中也看到了这种观察结果。从 Java 8 开始,您可以@FunctionalInterface
使用一种方法将注释添加到接口,这允许您使用 lambdas 创建该接口的实例。所以你可以用 Java 8 编写
@FunctionalInterface
interface Predicate<T> {
public abstract boolean predicate(T x);
public static Predicate<J> makeEqualsPredicate(J t) {
return (x) -> t.equals(x);
}
public static Predicate<J implements Comparable<J>> makeLessPredicate(J t) {
return (x) -> t.compareTo(x) < 0;
}
public Predicate<T> makeAndPredicate(Predicate<T> other) {
return (x) -> this.predicate(x) && other.predicate(x);
}
}
推荐阅读
- c++ - c ++相同的字符串不相等(实际上是char *)
- c# - 如何在c#中不反序列化读取和使用序列化的二进制数据
- sql - 从 SQL Server 生成 JSON
- permissions - 防止在单节点 slurm 设置中直接使用 GPU
- python - Pyomo 冷冻求解器
- amazon-web-services - 验证 AWS EC2 限制的准确方法
- karate - 如何验证 X509 证书身份验证的 karate.configure(SSL) 是否成功
- html - Bootstrap 4:如何在标签附近使用完整的跨度区域
- python - 将带有分组数据的 CSV 导入 Pandas 数据框
- swift - 背景颜色变化不可见