【问题标题】:Is there a solution to my problem using generics?使用泛型可以解决我的问题吗?
【发布时间】:2011-01-18 11:12:57
【问题描述】:

我正在使用 java.util.concurrency 框架实现一些代码。我会将一组可调用对象传递给一个类,该类将并行执行它们。我正在尝试找出以类型安全的方式获取每个响应的最佳方法。这里有一些代码可以帮助解释我在做什么:

首先我创建我的Callables,它们是要并行调用的工作单元。例如,这里第一个工作单元返回一个String,第二个工作单元返回一个Integer

Callable<String> firstCallable = new Callable<String>(){
   public String call() {...}
};

Callable<Integer> secondCallable = new Callable<Integer>(){
   public Integer call() {...}
};

现在我将它们触发到我的框架中以并行运行它们,诀窍是获取适当响应对象的句柄。这是一个有效的实现:

Map<Callable,Object> responseMap = ParallelSender.send(firstCallable, 
                                                       secondCallable);

其中Object 是特定Callable 的响应。因此,您可以这样做:

String firstCallableResponse = (String)responseMap.get(firstCallable);
Integer secondCallableResponse = (Integer)responseMap.get(secondCallable);

所以我的问题是,从地图获取时是否可以避免强制转换?这不会编译,但我正在考虑这些方面:

Map<Callable<T>, T> responseMap = ParallelSender.send(...);
String firstCallableResponse = responseMap.get(firstCallable);

这样返回的值基于Callable 键的类型参数。我担心的是,如果有人重构工作单元的返回类型(比如从IntegerBigDecimal 或其他),那么来自Object 的转换将永远不会被自动重构工具捕获,并可能导致运行时问题。


结论:感谢所有有用的 cmets 和下面的讨论,我采取了稍微不同的策略(尽管我认为 Sean Patrick Floyd 对我上面的问题的正确性)。我最终将响应映射全部删除并使用响应填充 Callable 对象。以下是相关代码sn-ps:

public abstract class AbstractParallelCallable<V> implements Callable<V> {

   /** The response generated by the Call method of this class. */
   private V callableResponse;

   public V getResponse() {
       return callableResponse;
   }

   public void setResponse(V response) {
       callableResponse = response;
   }
}

因此,我有一个抽象实现,它通过存储响应来包装 Callable 对象。接下来,在我的并行处理中,我得到每个 Future 创建的响应并填充 AbstractParallelCallable:

for (ParallelFutureTask<Object> future : futures) {
   try {
      future.getCallableHandle().setResponse(future.get());
   } catch(Exception e) {...}
}

其中 getCallableHandle 通过提供对 Callable 对象的引用返回 AbstractParallelCallable 对象和 ParallelFutureTask 包装器 FutureTask。执行后,调用代码可以这样做:

Integer theResult = firstCallable.getResponse();

【问题讨论】:

    标签: java generics map


    【解决方案1】:

    基于方法的方法

    您可以做到的唯一方法是将其封装在一个方法中:

    class ParallelSender{
    
        private final Map<Callable<?>, Object> map =
            new HashMap<Callable<?>, Object>();
    
        @SuppressWarnings("unchecked")
        public <T> T getResult(final Callable<T> callable){
            return (T) map.get(callable);
        }
    
    }
    

    客户端代码

    现在您的客户端代码不需要强制转换:

    ParallelSender parallelSender = new ParallelSender();
    Callable<Integer> integerCallable = new Callable<Integer>(){
    
        @Override
        public Integer call() throws Exception{
            return Integer.valueOf(1);
        }
    };
    Integer result = parallelSender.getResult(integerCallable);
    

    【讨论】:

    • 优秀。谢谢!我确信这是可能的。
    • 起初我对这个答案持怀疑态度,但我想如果您将地图保密,并维护 getResult 指定的合同,那么它看起来会正常工作。我个人很想完全封装地图以防止任何人破坏它。
    • @Dunes 是的,这里也一样。我会说得更清楚,但我有同事在门口等着吃午饭:-)
    【解决方案2】:

    快速回答:不,您不能在 Map 中存储多个类型,除非它们共享相同的父类型(例如 Object)。你也不希望能够做到这一点。我会考虑在这里重新考虑你的设计。是否真的需要将您的响应放入 Map 对象中?我假设您很快就会再次从地图中检索值。

    我可能会使用某种回调机制。一旦 Callable 完成,它就会在本地存储响应。当 ParralelSender 发送完所有(假设这将一次性返回所有 Callable 的结果)时,它会遍历每个 Callable 并告诉注册的侦听器处理结果。

    顺便说一句,我不确定我在那里是否有意义。我想举个例子,但很懒。

    【讨论】:

      【解决方案3】:

      如果您的所有回答都属于一种类型,那么就有了解决方案。

      Map<Callable<String>,String> responseMap = ParallelSender.send(firstCallable, 
                                                         secondCallable);
      String firstCallableResponse = responseMap.get(firstCallable);
      

      但是,如果您将不同的结果类型放在同一个映射中,并且您的代码取决于它们的实际类型,那么泛型将无法帮助您。

      【讨论】:

        【解决方案4】:

        类似这样的:

        Callable<String> firstCallable = new Callable<String>(){
           public String call() {...}
        };
        
        Callable<Integer> secondCallable = new Callable<Integer>(){
           public Integer call() {...}
        };
        
        
        class ParallelSender {
            ParallelResults send(Callable<Object> o,...) {...}
        }
        
        class Parallelresults {
            T <T> get(Callable<T> key) { ... }
        }
        
        ParallelResults res = ParallelSender.send(firstCallable, 
                                                  secondCallable);
        
        String s = res.get(firstCallable);
        Integer i = res.get(secondCallable);
        

        您可能需要在“get”方法中进行强制转换。

        【讨论】:

        • 您必须将其设为通用 (Parallelresults&lt;T&gt;) 才能编译
        • 实际上 泛型在方法上,这样它可以为每个调用返回不同的类型。 get(Calllable) 的实现当然必须进行未经检查的强制转换,但您只能在该库中放置一个 @SuppressWarning。
        • 我使用了
           块而不是缩进格式,它吃掉了尖括号中的所有内容......对stackoverflow不太熟练:-)
        猜你喜欢
        • 2010-11-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-02-07
        • 2023-03-19
        相关资源
        最近更新 更多