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

时间:2012-05-27 21:22:12

标签: java generics polymorphism overloading

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

utterlyDestroy(parameter) {
    parameter.crush()
}

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

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

我尝试使用泛型,但显然我不能写像

这样的东西
utterlyDestroy(<T instanceof Dreams || T instanceof Garlic> parameter)

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

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

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

8 个答案:

答案 0 :(得分:29)

这个怎么样:

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)。

答案 1 :(得分:6)

这么简单的事情怎么样?

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那么这就是你想要的

答案 2 :(得分:4)

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

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>类型的东西。

答案 3 :(得分:2)

只需使用方法重载。

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

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

如果您想以相同的方式支持这两种类型,可以为它们定义一个通用接口并使用泛型。

答案 4 :(得分:2)

您可以使用界面和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 :(得分:1)

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

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

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

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

但是,如果一个人不是另一个人的子类型,那么从Garlic转向Dream不是一个选择。

答案 6 :(得分:1)

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

答案 7 :(得分:0)

我正在使用:

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

时才有效