接受两种不同类型作为参数的方法
我正在编写一个方法,它应该接受两个类型之一的对象作为其参数,这两个类型不共享除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时才有效