我认为the answer provided by @RyanTheLeach 通过基于注册表的方法很好地解决了您的问题。但是,在您的问题的 cmets 中,提出了 visitor pattern。我提到了“如果你自己创建实例”,并且......目前任何现有答案都没有详细说明。我发现您在这里遇到的问题的细微变化往往非常频繁。由于在 Java 中实现基于包装器的访问者模式可能有点令人困惑,我想我会添加一些有关该特定方法的细节。
首先,这似乎应该非常简单。只需创建一个访问者接口、一个基本类型的包装器和一个接受访问者实现的包装器方法。
public interface AnimalVisitor {
public void visit(Animal instance);
public void visit(Cat instance);
public void visit(Dog instance);
// ...
}
public class AnimalWrapper {
public final Animal instance;
// ...
public void acceptVisitor(AnimalVisitor visitor) {
visitor.visit(instance);
}
}
方法重载会处理剩下的事情,对吗?事实证明这不起作用,因为Java determines the method overload to call based on the statically determined type,而不是运行时的实际类型。请注意,这与继承不同,其中dynamic dispatch 用于在运行时查找virtual method 的具体实现。
也许我们可以用泛型来解决这个问题?
public class AnimalWrapper <T extends Animal> {
public final T instance;
// ...
public void acceptVisitor(AnimalVisitor visitor) {
visitor.visit(instance);
}
}
不。它可能看起来像对visitor.visit(instance) 的调用应该静态确定子类型,但实际上泛型的静态类型是其捕获的上限(在本例中为Animal)。
也许我们可以通过利用通常的 Java 模式来获取runtime type information for generics 来解决这个问题?
public class AnimalWrapper <T extends Animal> {
public final T instance;
private final Class<T> type;
// ...
public void acceptVisitor(AnimalVisitor visitor) {
visitor.visit(type.cast(instance));
}
}
很遗憾,由于与以前相同的原因,这也不起作用; Class<T> type 也是通用的,因此它静态解析为基本类型 Animal。
那么我们怎样才能使这个工作呢? This answer 非常清楚地说明了如何为要包装的每个子类型使用单独的包装类来解决此问题。如果我们可以在不需要这么多不同的独特包装类的情况下完成它,那不是很好吗?你说你使用的是 Java 8,所以让我们结合 lambdas 和静态工厂方法来或多或少透明地实现所有这些(并且只使用一个包装类)。
第三方代码
public abstract class Animal {
private final String instanceName;
private Animal(String instanceName) {
this.instanceName = instanceName;
}
public abstract String typeName();
public String instanceName() {
return instanceName;
}
public static class Cat extends Animal {
public Cat(String instanceName) {
super(instanceName);
}
@Override
public String typeName() {
return "Cat";
}
}
public static class Dog extends Animal {
public Dog(String instanceName) {
super(instanceName);
}
@Override
public String typeName() {
return "Dog";
}
}
public static class Fox extends Animal {
public Fox(String instanceName) {
super(instanceName);
}
@Override
public String typeName() {
return "Fox";
}
}
}
我们的准则
/*
* The Wrapper Class
*/
public class AnimalWrapper {
private final Animal instance;
private final Consumer<AnimalVisitor> visitResolver;
private AnimalWrapper(Animal instance, Consumer<AnimalVisitor> visitResolver) {
this.instance = instance;
this.visitResolver = visitResolver;
}
public Animal getInstance() {
return instance;
}
public void acceptVisitor(AnimalVisitor visitor) {
visitResolver.accept(visitor);
}
public static AnimalWrapper create(Animal instance) {
final Consumer<AnimalVisitor> visitResolver = visitor -> AnimalVisitor.visitResolver(visitor, instance);
return new AnimalWrapper(instance, visitResolver);
}
public static AnimalWrapper create(Animal.Cat instance) {
final Consumer<AnimalVisitor> visitResolver = visitor -> AnimalVisitor.visitResolver(visitor, instance);
return new AnimalWrapper(instance, visitResolver);
}
public static AnimalWrapper create(Animal.Dog instance) {
final Consumer<AnimalVisitor> visitResolver = visitor -> AnimalVisitor.visitResolver(visitor, instance);
return new AnimalWrapper(instance, visitResolver);
}
public static AnimalWrapper create(Animal.Fox instance) {
final Consumer<AnimalVisitor> visitResolver = visitor -> AnimalVisitor.visitResolver(visitor, instance);
return new AnimalWrapper(instance, visitResolver);
}
}
/*
* The Visitor Interface
*/
public interface AnimalVisitor {
public default void visit(Animal instance) {
printMessage("Default implementation", "Animal (base type)", instance);
}
public static void visitResolver(AnimalVisitor visitor, Animal instance) {
visitor.visit(instance);
}
public default void visit(Animal.Cat instance) {
printMessage("Default implementation", "Cat", instance);
}
public static void visitResolver(AnimalVisitor visitor, Animal.Cat instance) {
visitor.visit(instance);
}
public default void visit(Animal.Dog instance) {
printMessage("Default implementation", "Dog", instance);
}
public static void visitResolver(AnimalVisitor visitor, Animal.Dog instance) {
visitor.visit(instance);
}
public static void printMessage(String implementation, String signature, Animal instance) {
System.out.println();
System.out.println(implementation);
System.out.println("\tSignature: " + signature);
System.out.println("\tInstance type: " + instance.typeName());
System.out.println("\tInstance name: " + instance.instanceName());
}
}
/*
* The Visitor Implementation
*/
public class AnimalVisitorImpl implements AnimalVisitor {
@Override
public void visit(Animal instance) {
AnimalVisitor.printMessage("Specialized implementation", "Animal (base type)", instance);
}
@Override
public void visit(Animal.Cat instance) {
AnimalVisitor.printMessage("Specialized implementation", "Cat", instance);
}
}
/*
* Actual Usage
*/
public static void main(String[] args) {
final List<AnimalWrapper> wrappedAnimals = new ArrayList<>();
wrappedAnimals.add(AnimalWrapper.create(new Animal.Cat("A normal cat.")));
wrappedAnimals.add(AnimalWrapper.create((Animal) new Animal.Cat("A stealthy cat.")));
wrappedAnimals.add(AnimalWrapper.create(new Animal.Dog("A dog (only default support).")));
wrappedAnimals.add(AnimalWrapper.create(new Animal.Fox("A fox (no support).")));
final AnimalVisitor visitor = new AnimalVisitorImpl();
for (AnimalWrapper w : wrappedAnimals)
w.acceptVisitor(visitor);
}
输出
专业实施
签名:猫
实例类型:猫
实例名称:一只普通的猫。
专业实施
签名:动物(基础类型)
实例类型:猫
实例名称:一只隐形猫。
默认实现
签名:狗
实例类型:狗
实例名称:一只狗(仅默认支持)。
专业实施
签名:动物(基础类型)
实例类型:狐狸
实例名称:一只狐狸(不支持)。
其他想法
- 图书馆应该首先提供一种接受访问者的方法!
- 所有这些只有在我们实例化单个实例时才有效。一旦我们不得不处理从第三方代码接收不透明的东西(例如
Collection<Animal>),这种方法就会失效,我们发现自己又回到了基于检查和转换(或反射)的方法。这就是registry based approach outlined by @RyanTheLeach 真正闪耀的原因; animalInstance.getClass() 返回由该特定子类型的所有实例共享的唯一静态类对象,从而使我们即使在呈现基类型实例时也能检索到正确的 lambda。