【问题标题】:Appropriate use of Java generics on collection在集合中适当使用 Java 泛型
【发布时间】:2013-04-28 22:41:23
【问题描述】:

我以前从来没有机会玩过泛型(比如编写泛型类),但现在需要,我遇到了一些困惑。

有这个接口,它是 something 的包装器。实现不是集合,因此,每个实例只能访问一个 something

public interface Resource<T> {
    // Expected operations:
    void write(ResourceState state);
    ResourceState read();
}

作为实现,我希望有一个ExclusiveResource&lt;T&gt; 和一个ShareableResource&lt;T&gt;,它们主要/仅在使用的锁定方案(分别为常规锁和读写锁)上有所不同。
至于readwrite是如何执行的,我打算使用Strategy模式。
例如,我可能有

// This would implement a Strategy<File>.
FileStrategy fs = new FileStrategy();
Resource<File> r = new ExclusiveResource<File>(fs);

现在,我还获得了这些资源的某种集合,例如资源池。
我想将一个键映射到资源池中的每个资源,并且我想添加、检索和删除资源,但我不确定如何声明映射和方法。我尝试了以下方法:

public class ResourcePool {
    // instance variables
    private final Map<String, Resource<?>> map;

    /** Empty constructor of objects of class ResourcePool. */
    public ResourcePool() {
        map = new HashMap<String, Resource<?>>();
    }

    /** */
    public Resource<?> get(String s) {
        return map.get(s);
    }

    /** */
    public void add(String s, Resource<?> r) {
        map.put(s, r);
    }

    // ...
}

这似乎不是最合适的方法,引用 Josh Bloch 的话,Effective Java Reloaded

用户无需考虑通配符即可使用您的 API。

我用以下方法测试了这段代码:

public static void test() {
    ResourcePool rp = new ResourcePool();

    Resource<String> r1 = new ShareableResource<>("test");
    Resource<Integer> r2 = new ShareableResource<>(1);
    Resource<List<String>> r3 = new ShareableResource<>(
            Arrays.asList(new String[]{"1", "2"})
    );

    // These are all ok.
    rp.add("1", r1);
    rp.add("2", r2);
    rp.add("3", r3);

    // This results in a compiler error (incompatible types).
    Resource<String> g1 = rp.get("1");

    // This results in a compiler warning (unsafe operation).
    Resource<String> g2 = (Resource<String>) rp.get("1");
}

我不喜欢这样,当代码编译时出现警告。让我感到内疚,并且似乎暗示了糟糕的编码。

所以,我的问题是我应该如何处理这种情况。
这是做我想做的事情的正确方法吗?
这样做可以做到没有不安全的操作吗?

【问题讨论】:

  • 我认为有必要查看 Resource 的实现来解决这个问题。我认为你最好的选择是从资源中删除泛型类型,只使用普通接口。
  • @KevinBowersox 我没有提供有关资源的更多详细信息,因为担心问题可能会太长,但我会用我打算如何处理资源以及什么来更新它预计会实施。
  • 我不确定有什么方法可以避免不安全的演员表。想想你熟悉的泛型类型,例如List&lt;E&gt;。现在想象一下有一个List&lt;List&lt;?&gt;&gt;,你怎么能以一种类型安全的方式使用这个参数化类型呢?我不确定你能不能。除非您非常了解列表中每个项目所包含的内容,否则您永远不会知道列表返回的类型

标签: java generics collections wildcard


【解决方案1】:

我认为没有任何方法可以避免使用您的设计进行未经检查的强制转换。也就是说,您可以避免每次检索 Resource 时都必须进行强制转换:

@SuppressWarnings("unchecked")
public <T> Resource<T> get(String s, Class<T> c) {
    return (Resource<T>) map.get(s);
}

当您想要检索 Resource 时,您可以传入所需的类,如下所示:

Resource<String> g1 = rp.get("1", String.class);

不过,您应该小心这种设计,因为运行时无法保证返回的 Resource 实际上是 Resource&lt;String&gt;

【讨论】:

  • 是的,我认为没有办法避免这一切。但是,现在你让我想我是否真的需要泛型。这应该是可扩展的,所以我认为,通过使用泛型,我可以在构造函数中询问每种资源的特定策略(请参阅更新的问题)。
  • @afsantos 我会添加另一个答案,因为这是解决问题的不同方法
  • 即使我不确定我是否会使用此建议,但我会将您的答案标记为已接受。我的想法转移到资源的设计和使用上,我将就此发布另一个问题。
【解决方案2】:

您可以为所需的每种类型的资源创建不同的集合,并将ResourcePool 设为通用:

ResourcePool<String> stringpool = new ResourcePool<String>();
ResourcePool<Integer> intpool = new ResourcePool<Integer>();

这将为您提供编译时检查类型的好处。并且似乎无论何时您从ResourcePool 中得到一些东西,您都知道自己想要什么类型,因此您可以选择合适的集合。​​

【讨论】:

    猜你喜欢
    • 2013-11-19
    • 1970-01-01
    • 2017-12-24
    • 2016-07-26
    • 2012-02-06
    • 1970-01-01
    • 1970-01-01
    • 2011-09-16
    • 2019-09-06
    相关资源
    最近更新 更多