Java 中的多态非柯里化方法调用(临时多态)
Polymorphic uncurried method calls (adhoc polymorphism) in Java
让我从一个例子开始。
假设我有一个摘要 Vehicle
class.
public abstract class Vehicle {
public Vehicle() {}
public abstract void ride();
}
和class是Car
和Bicycle
继承自这个摘要class。
public class Car extends Vehicle {
public Car() {}
@Override
public void ride() {
System.out.println("Riding the car.");
}
}
public class Bicycle extends Vehicle {
public Bicycle() {}
@Override
public void ride() {
System.out.println("Riding the bicycle.");
}
}
当我将 ride()
方法应用于类型 Vehicle
的对象时,其实际类型只能在运行时确定,JVM 将应用 ride()
的正确版本。
也就是说,在 v.ride()
类型的柯里化方法调用中,多态性以预期的方式工作。
但是如果我有一个外部实现,其形式为只接受 Vehicle
的子类型作为参数的方法呢?那么,如果我有 repair(Bicycle b)
和 repair(Car c)
方法呢?未柯里化的多态方法调用 repair(v)
将不起作用。
示例:
import java.util.ArrayList;
import java.util.List;
public class Main {
private static void playWithVehicle() {
List<Vehicle> garage = new ArrayList<Vehicle>();
garage.add(new Car());
garage.add(new Car());
garage.add(new Bicycle());
garage.forEach((v) -> v.ride()); // Works.
garage.forEach((v) -> {
/* This would be nice to have.
repair(v.castToRuntimeType());
*/
// This is an ugly solution, but the obvious way I can think of.
switch (v.getClass().getName()) {
case "Bicycle":
repair((Bicycle) v);
break;
case "Car":
repair((Car) v);
break;
default:
break;
}
});
}
private static void repair(Bicycle b) {
System.out.println("Repairing the bicycle.");
}
private static void repair(Car c) {
System.out.println("Repairing the car.");
}
public static void main(String[] args) {
playWithVehicle();
}
}
我必须检查 class 名称和沮丧。对此有更好的解决方案吗?
编辑:我的实际目的是遍历一个抽象语法树,我碰巧注意到我想要双重分派。
Ast
是一个抽象 class,像 Assign
、MethodCall
或 ReturnStmt
这样的实际 AST 节点从中继承。 body
是 Ast
的多态列表。
代码片段:
List<Ast> body;
body.parallelStream().forEach((ast) -> {
// This one won't work.
visit(ast);
// This one will work.
if (ast instanceof Assign) {
visit((Assign) ast);
} else if (ast instance of MethodCall) {
visit((MethodCall) ast);
} else if (ast instance of ReturnStmt) {
visit((ReturnStmt) ast);
}
// etc. for other AST nodes
});
private void visit(Assign ast) {
}
private void visit(MethodCall ast) {
}
private void visit(ReturnStmt ast) {
}
我实现双重调度的唯一可能性是检查 class 和向下转型或正确实施访问者模式,对吗?
回答:Java没有multiple dispatch,可以用instanceof
或者visitor pattern来模拟
看这里:
Java method overloading + double dispatch
另见此处:https://en.wikipedia.org/wiki/Multiple_dispatch#Examples_of_emulating_multiple_dispatch
旁注,这在 C# 中完全可以通过 dynamic
调用:How to build double dispatch using extensions
这在编译为 JVM 字节码的许多语言中也是可能的,例如Groovy 被提及。
让我从一个例子开始。
假设我有一个摘要 Vehicle
class.
public abstract class Vehicle {
public Vehicle() {}
public abstract void ride();
}
和class是Car
和Bicycle
继承自这个摘要class。
public class Car extends Vehicle {
public Car() {}
@Override
public void ride() {
System.out.println("Riding the car.");
}
}
public class Bicycle extends Vehicle {
public Bicycle() {}
@Override
public void ride() {
System.out.println("Riding the bicycle.");
}
}
当我将 ride()
方法应用于类型 Vehicle
的对象时,其实际类型只能在运行时确定,JVM 将应用 ride()
的正确版本。
也就是说,在 v.ride()
类型的柯里化方法调用中,多态性以预期的方式工作。
但是如果我有一个外部实现,其形式为只接受 Vehicle
的子类型作为参数的方法呢?那么,如果我有 repair(Bicycle b)
和 repair(Car c)
方法呢?未柯里化的多态方法调用 repair(v)
将不起作用。
示例:
import java.util.ArrayList;
import java.util.List;
public class Main {
private static void playWithVehicle() {
List<Vehicle> garage = new ArrayList<Vehicle>();
garage.add(new Car());
garage.add(new Car());
garage.add(new Bicycle());
garage.forEach((v) -> v.ride()); // Works.
garage.forEach((v) -> {
/* This would be nice to have.
repair(v.castToRuntimeType());
*/
// This is an ugly solution, but the obvious way I can think of.
switch (v.getClass().getName()) {
case "Bicycle":
repair((Bicycle) v);
break;
case "Car":
repair((Car) v);
break;
default:
break;
}
});
}
private static void repair(Bicycle b) {
System.out.println("Repairing the bicycle.");
}
private static void repair(Car c) {
System.out.println("Repairing the car.");
}
public static void main(String[] args) {
playWithVehicle();
}
}
我必须检查 class 名称和沮丧。对此有更好的解决方案吗?
编辑:我的实际目的是遍历一个抽象语法树,我碰巧注意到我想要双重分派。
Ast
是一个抽象 class,像 Assign
、MethodCall
或 ReturnStmt
这样的实际 AST 节点从中继承。 body
是 Ast
的多态列表。
代码片段:
List<Ast> body;
body.parallelStream().forEach((ast) -> {
// This one won't work.
visit(ast);
// This one will work.
if (ast instanceof Assign) {
visit((Assign) ast);
} else if (ast instance of MethodCall) {
visit((MethodCall) ast);
} else if (ast instance of ReturnStmt) {
visit((ReturnStmt) ast);
}
// etc. for other AST nodes
});
private void visit(Assign ast) {
}
private void visit(MethodCall ast) {
}
private void visit(ReturnStmt ast) {
}
我实现双重调度的唯一可能性是检查 class 和向下转型或正确实施访问者模式,对吗?
回答:Java没有multiple dispatch,可以用instanceof
或者visitor pattern来模拟
看这里: Java method overloading + double dispatch
另见此处:https://en.wikipedia.org/wiki/Multiple_dispatch#Examples_of_emulating_multiple_dispatch
旁注,这在 C# 中完全可以通过 dynamic
调用:How to build double dispatch using extensions
这在编译为 JVM 字节码的许多语言中也是可能的,例如Groovy 被提及。