动态强制转换类并调用适当的方法

时间:2015-12-13 21:39:40

标签: java reflection

我正在编写一个实用程序,它使用我无法控制的第三方库中定义的某些类。

我想知道处理如下所述情况的好方法: 第三方图书馆有一个基础抽象类' Food'由“开胃菜”,“主菜”,“饮料'饮料'和'甜点'。 (第三方图书馆的所有部分)

我正在写一个' WaiterUtility'有各种食物的方法。 我想避免无休止的 instanceof 检查链。

`

Class WaiterUtility{
   public serveItems(Food[] items)
   {
        for(Food aFood : items){
            //how do i call the sub-class specific methods i wrote below?
        }
    }
    private void serve(Appetizer aFood){//somecode}
    private void serve(Entree aFood){//somecode}
    private void serve(Beverage aFood){//somecode}
    private void serve(Dessert aFood){//somecode}
}

`

3 个答案:

答案 0 :(得分:1)

如果可能的话,我会恳求你不要使用反射,因为TBotV63在他的回答中做了(他甚至说要避免它)。来自Oracle documentation

  

如果可以在不使用反射的情况下执行操作,则最好避免使用它。

所以,显然我们倾向于说可以提供所有Food,并且任何Waiter都可以提供任何类型的Food。因此,理想情况下,一个好的API会暴露出足以使serve(Food)方法完成工作的方法,而不知道它是什么类型的食物。似乎你的问题暗示事实并非如此,因此需要做更多的事情 如果第三方库接受社区输入,那么您应该尝试打开问题或拉取请求以添加功能 显然,这并不总是可行的,所以接下来最好的做法是创建一个接口(类似Serveable)来定义您需要的方法,然后在实现该接口时子类化不同类型的食物。然后你会Waiter.serve(Serveable)

这比instanceof的反射或许多用途更多的工作,但它是更好的OO设计。

为什么反思不好

反思文档指出了反思的3个缺点

  1. 内部暴露
  2. 性能
  3. 安全
  4. 虽然你可能不关心2或3,但1尤其糟糕。

      

    ...使用反射可以...渲染代码功能失常,可能会破坏可移植性。反射代码打破了抽象,因此可能会通过升级平台来改变行为。

    为什么instanceof不好(在这种情况下)

    serveItems(Food[])向调用者暗示,如果您将多个Food项传递给它,它将为每个项目提供服务。然而事实并非如此。我们只能提供Food的某些子类,如果我们尝试其他任何操作,我们将遇到运行时错误。 Java是一种很好的类型安全语言,我们比运行时错误更喜欢编译时错误 另一个缺点是,每次添加或更改Waiter的新子类时,都需要将额外的代码添加到Food。这成为一个贯穿各领域的问题,从发展的角度来看,代码不可扩展 这绝不是唯一的缺点/问题,只是几个例子。

答案 1 :(得分:0)

尝试类似以下的内容:

public serveItems(Food[] items)
{
    for(Food aFood : items){
        Class<?> foodClass = aFood.getClass(); // Get the food's class
        Method serve = WaiterUtility.class.getMethod("serve", foodClass); // Get the method by name and argument types

        try {
          serve.invoke(this, aFood);
        } catch (IllegalArgumentException e) { // Should never occur, we're matching it up.
        } catch (IllegalAccessException e) { // Shouldn't occur, we're in the same class.
        } catch (InvocationTargetException e) {
            // Handle errors possibly thrown by the serve method.
        }
}

没有测试过这个。

请注意,你应该避免这种情况,这是一种糟糕的设计。

答案 2 :(得分:0)

您可以尝试以下代码:

Class WaiterUtility{
   private Map<Class<? extends Food>, Waiter> waiters = new HashMap<>(); 

   WaiterUtility() {
     waiters.put(Appetizer.class, new AppetizerWaiter());
     waiters.put(Entree.class, new EntreeWaiter());
     waiters.put(Beverage.class, new BeverageWaiter());
     waiters.put(Dessert.class, new DessertWaiter());
   }

   public serveItems(Food[] items)
   {
        for(Food aFood : items){
            waiter.get(aFood.getClass()).serve(aFood);
        }
    }

    private static abstract interface Waiter  {
       private void serve(Food aFood);
    }

    private static class AppetizerWaiter implements Waiter  {
       private void serve(Food aFood){
         Appetizer appetizer = (Appetizer) aFood;
         //somecode
      }
    }

    private static class EntreeWaiter implements Waiter  {
       private void serve(Food aFood){//somecode}
    }

    private static class BeverageWaiter implements Waiter  {
       private void serve(Food aFood){//somecode}
    }

    private static class DessertWaiter implements Waiter {
       private void serve(Food aFood){//somecode}
    }
}