接受两种不同类型作为参数的方法
Posted
技术标签:
【中文标题】接受两种不同类型作为参数的方法【英文标题】:Method accepting two different types as parameter 【发布时间】:2012-06-02 09:24:26 【问题描述】:我正在编写一个方法,它应该接受两个类型之一的对象作为其参数,这两种类型的对象除了 Object 之外不共享父类型。例如,类型是 Dreams 和 Garlic。您可以同时使用dreams.crush()
和garlic.crush()
。我想要一个方法utterlyDestroy(parameter)
,它可以接受 Dreams 和 Garlic 作为其参数。
utterlyDestroy(parameter)
parameter.crush()
Garlic 和 Dreams 都是某个库的一部分,因此不能让它们实现 ICrushable 接口(以便我可以编写 utterlyDestroy(ICrushable parameter)
)。
我的方法体很长,所以重载意味着重复代码。丑陋的。 我确信我可以使用反射并进行一些类黑客攻击。丑。
我尝试使用泛型,但显然我不能写类似的东西
utterlyDestroy(<T instanceof Dreams || T instanceof Garlic> parameter)
是否可以将 Garlic 类型转换为 Dreams?
utterlyDestroy(Object parameter)
((Dreams)parameter).crush()
但这仍然很丑陋。我还有哪些其他选择?处理这种情况的首选方法是什么?
【问题讨论】:
【参考方案1】:这个怎么样:
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 接口,但对于现有的类,参数被包装在 ICrushable 中并传递给完成所有工作的 utterlyDestroy(ICrushable)。
【讨论】:
呃,那天好像有人有一种破坏性的情绪。【参考方案2】:这么简单的事情怎么样?
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;
garlic.crush();
// Here you can use a Garlic
如果utterlyDestroy
太复杂和太大,而您只想调用crush
,那么这就是您想要的。
【讨论】:
我自然想到了,但是使用 Object 作为参数似乎是错误的。 你不是在重复同样的代码吗?对我来说没有意义。【参考方案3】:您可以在 Java 中实现 Haskell 式的 Either 类;像这样:
class Either<L,R>
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<Dreams, Garlic>
类型的东西。
【讨论】:
【参考方案4】:你可以使用一个接口和adapt你的类型。
界面:
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);
但是,与反射一样,您牺牲了编译时类型检查。
【讨论】:
【参考方案5】:如果您要在项目的许多地方以相同的方式对待它们,我建议将它们包装在一个类中,比如适配器。
【讨论】:
不,它只在一个地方。不过感谢您的回答。【参考方案6】:只需使用方法重载。
public void utterlyDestroy(Dreams parameter)
parameter.crush();
public void utterlyDestroy(Garlic parameter)
parameter.crush();
如果您想以相同的方式支持多于这两种类型,您可以为它们都定义一个通用接口并使用泛型。
【讨论】:
正如我所说,我的方法很长,这意味着重复代码。 @JohnEye - 那么只在两个类中调用接口。如果您无法正确构建类层次结构,另请参阅 AadvarkSoup 的答案。【参考方案7】:创建 Interface Crushable 似乎是最干净的方法。是否可以选择 Garlic 或 Dreams 子类型,并将您的界面添加到子类型中?
除此之外,您可以将公共代码放在私有方法中,并让两个版本的 utterlyDestroy 在调用公共代码之前对各个对象执行它们必须执行的操作。如果您的方法体很长,则可能无论如何都需要将其分解为私有方法。不过,我猜您已经想到了这一点,因为它比添加接口更为明显。
您可以将参数作为对象引入,然后进行强制转换。这就是你所说的反射吗?即,
public void utterlyCrush(Object crushable)
if (crushable instanceOf Dream)
...
if (curshable instanceOf Garlic)
...
但考虑到一个不是另一个的子类型,从 Garlic 转换为 Dream 不是一种选择。
【讨论】:
不,通过反思,我的意思是使用一些漂亮的功能来找到一个名为 crush() 的方法并执行它或类似的东西。我写这些只是为了阻止“反射巨魔”回答。【参考方案8】:因为我正在使用:
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 时才有效
【讨论】:
以上是关于接受两种不同类型作为参数的方法的主要内容,如果未能解决你的问题,请参考以下文章
thymeleaf 接受字符串类型model值作为js方法参数报错500