Ire*_*ina 3 java oop polymorphism functional-programming dynamic-dispatch
我来自函数式语言(例如 Haskell),我非常喜欢使用类型类来实现多态,这也是实现临时重载的结构方法。
然而,最近我开始理解OOP对实际问题进行建模的方式,我很好奇为什么我们需要OOP 语言(例如 Java)中的动态多态性。根据我的经验,大多数函数调用可以在编译时解决,因为许多函数式语言不支持子类型。
所以我的问题是,在什么样的情况下我们必须使用动态多态而不是编译时多态?我的猜测是:
在 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);
}
}
Run Code Online (Sandbox Code Playgroud)
在 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 (&&)
Run Code Online (Sandbox Code Playgroud)
Java 允许通过子类化来“动态分派”方法。Haskell 允许通过高阶函数“动态调度”函数。
但是等等,你说。Predicate 是一个只有一个方法的接口。如果我们想要两种方法,我们应该怎么做?
那么,如果一个方法的接口对应一个函数,那么两个方法的接口必须对应一对函数。这就是被称为“组合优于继承”的 OOP 原则。
所以我们总是可以用 Haskell 风格的高阶函数代替 Java 风格的动态多态。
事实上,您实际上也在现代 Java 中看到了这种观察结果。从 Java 8 开始,您可以@FunctionalInterface
使用一种方法将注解添加到接口,这允许您使用 lambda 创建该接口的实例。所以你可以用 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);
}
}
Run Code Online (Sandbox Code Playgroud)