接受两种不同类型作为参数的方法

我正在编写一个方法,它应该接受两个类型之一的对象作为其参数,这两个类型不共享除Object之外的父类型。 例如,类型是梦想和大蒜。 你可以做dreams.crush()garlic.crush() 。 我想有一个方法utterlyDestroy(parameter) ,它接受Dreams和Garlic作为它的参数。

 utterlyDestroy(parameter) { parameter.crush() } 

Garlic和dream都是某些库的一部分,所以让它们实现一个接口ICrushable(这样我就可以编写utterlyDestroy(ICrushable parameter) )不是一个选项。

我的方法体很长,所以重载就意味着重复代码。 丑陋。 我相信我可以使用reflection并做一些类黑客攻击。 丑陋。

我尝试使用generics,但显然我不能写类似的东西

 utterlyDestroy( parameter) 

是否有可能将大蒜强加给梦想?

 utterlyDestroy(Object parameter) { ((Dreams)parameter).crush() } 

但这仍然是丑陋的。 我的其他选择是什么?处理这种情况的首选方法是什么?

这个怎么样:

 interface ICrushable { void crush(); } utterlyDestroy(ICrushable parameter) { // Very long crushing process goes here parameter.crush() } utterlyDestroy(Dreams parameter) { utterlyDestroy(new ICrushable() { crush() {parameter.crush();}); } utterlyDestroy(Garlic parameter) { utterlyDestroy(new ICrushable() { crush() {parameter.crush();}); } 

新开发应该实现ICrushable接口,但对于现有的Classes,参数包装在ICrushable中并传递给完成所有工作的utterlyDestroy(ICrushable)。

这么简单的事怎么样?

 utterlyDestroy(Object parameter) { if(parameter instanceOf Dreams){ Dream dream = (Dreams)parameter; dream.crush(); //Here you can use a Dream } else if(parameter instanceOf Garlic){ Garlic garlic = (Garlic)parameter; //Here you can use a Garlic garlic.crush(); } } 

如果utterlyDestroy过于复杂和庞大而你只想打电话给crush那么这就是你想要的

您可以在Java中实现Haskell-esque Either-class; 像这样的东西:

 class Either { private Object value; public static enum Side {LEFT, RIGHT} public Either(L left) {value = left;} public Either(R right) {value = right;} public Side getSide() {return value instanceof L ? Side.LEFT : Side.RIGHT;} // Both return null if the correct side isn't contained. public L getLeft() {return value instanceof L ? (L) value : null;} public R getRight() {return value instanceof R ? (R) value : null;} } 

然后你让那个方法采用Either类型的东西。

只需使用方法重载。

 public void utterlyDestroy(Dreams parameter) { parameter.crush(); } public void utterlyDestroy(Garlic parameter) { parameter.crush(); } 

如果要以相同的方式支持多于这两种类型,可以为它们全部定义一个公共接口并使用generics。

您可以使用界面并使您的类型适应它。

接口:

 public interface Crushable { public void crush(); } 

示例调用:

 public class Crusher { public static void crush(Crushable crushable) { crushable.crush(); } } 

示例适配器工厂方法:

 public final class Dreams { public static Crushable asCrushable(final Dream dream) { class DreamCrusher implements Crushable { @Override public void crush() { dream.crush(); } } return new DreamCrusher(); } private Dreams() {} } 

消费者代码如下所示:

  Dream dream = new Dream(); Crushable crushable = Dreams.asCrushable(dream); Crusher.crush(crushable); 

如果你有许多类型可以适应,你可以考虑反思。 这是一个使用Proxy类型的(未优化的)适配器工厂:

 public final class Crushables { private static final Class[] INTERFACES = { Crushable.class }; public static Crushable adapt(final Object crushable) { class Handler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return crushable.getClass() .getMethod(method.getName(), method.getParameterTypes()) .invoke(crushable, args); } } ClassLoader loader = Thread.currentThread() .getContextClassLoader(); return (Crushable) Proxy.newProxyInstance(loader, INTERFACES, new Handler()); } private Crushables() {} } 

对于API消费者来说,这并不难看:

  Dream dream = new Dream(); Crushable crushable = Crushables.adapt(dream); Crusher.crush(crushable); 

但是,正如reflection一样,您牺牲了编译时类型检查。

如果您打算在项目的许多地方以相同的方式对待它们,我建议将它们包装在一个类中,比如适配器。

创建界面Crushable似乎是最干净的方式。 将Garlic或Dreams子类型选为子选项,并将接口添加到子类型中吗?

除此之外,您可以将公共代码放在私有方法中,并且在调用公共代码之前,让两个版本的utterlyDestroy执行他们必须对单个对象执行的操作。 如果你的方法体长,可能需要将其分解为私有方法。 我猜你已经想到了这一点,因为它比添加一个接口更加明显。

您可以将参数作为Object引入,然后进行转换。 这是你反思的意思吗? 即

 public void utterlyCrush(Object crushable) { if (crushable instanceOf Dream) { ... } if (curshable instanceOf Garlic) { ... } 

但是,从大蒜到梦的铸造不是一个选择,因为一个不是另一个的子类型。

我正在使用:

 void fooFunction(Object o){ Type1 foo=null; if(o instanceof Type1) foo=(Type1)o; if(o instanceof Type2) foo=((Type2)o).toType1(); // code } 

但是只有在Type2可以转换为Type1时才有效