泛型问题

时间:2010-07-16 21:40:47

标签: java generics casting

我收到关于“return(T)值”未经检查的强制转换的警告;线。有更好的方法可以做到这一点,还是应该抑制警告?

class SomeClass<T>
{
    /* ... other methods ... */

    private Set<T> aSet;
    public T filter(Object value)
    {
        if (this.aSet.contains(value))
            return (T) value;
        else
            return null;
    }
}

编辑:我坚持使用public T filter(Object value)作为签名。

7 个答案:

答案 0 :(得分:4)

如何使用泛型类型参数作为要过滤的参数类型。

class SomeClass
{
    /* ... other methods ... */

    private Set<T> aSet;
    public T filter(T value)
    {
        if (this.aSet.contains(value))
            return (T) value;
        else
            return null;
    }
}

好的,既然你坚持使用object签名,那么你没有其他任何机会,但是禁用/忽略警告。 Java Generics在底层类型系统支持它们的意义上不是“真正的泛型”。实际上,它们只是一个编译器,因为它们基于Type Erasure。性能损失和可能不安全的转换是维护与旧版JVM的二进制兼容性的代价。

你可以将它与具有真正泛型的.NET CLR进行对比,我写了blog post比较最近的两种方法,如果我上面提到的任何内容让你感到困惑,你也可以参考这两种方法。 / p>

答案 1 :(得分:3)

在这种情况下,您可以取消警告并发表评论保存的原因:

   if (this.aSet.contains(value)) {
        // this following cast from Object to T is save, because ...
        @SuppressWarnings("unchecked") T result = (T) value;
        return result;
   } else
        return null;

有一种替代方案,但这需要,类或方法'知道'它的参数化类型。然后我们可以在没有警告的情况下施放(但也应该发表评论)。我展示了一个解决方案,我们引入/更改构造函数以将“泛型”信息存储为字段:

public class SomeClass<T> {

    private Set<T> aSet;

    private Class<T> genericType;
    public SomeClass(Class<T> genericType) {
        this.genericType = genericType;
    }

    public T filter(Object value)
    {
        if (this.aSet.contains(value))
            // this following cast from Object to T is save, because ...
            return genericType.cast(value);
        else
            return null;
    }
}

答案 2 :(得分:2)

有可能:)

public T filter(Object value)
{
    if (this.aSet.contains(value)) {
        Set<T> tmp = new TreeSet<T>(aSet);
        tmp.retainAll(Collections.singleton(value));
        return tmp.iterator().next();
    }
    return null;
}

但这显然比做演员更丑陋。

答案 3 :(得分:2)

也许你可以替换:

private Set<T> aSet;

private final Map<T,T> aSet;

Set可能会以Map的形式实现。

答案 4 :(得分:2)

阐述Tom Hawtin的回答,您可以选择使用Map<T,T>来解决投射问题。如果您使用HashSet<T>进行aSet的实施,HashSet无论如何都会在幕后使用HashMap<T,HashSet<T>>(它使用对自身的引用作为值 - 不确定是否除了选择之外,还有一个原因 - 将集合的“值”作为键)并通过重新解释Map函数的返回值来执行大部分操作。

因此,如果您愿意,可以执行此操作(并且我没有立即明白为什么它的性能会比HashSet更低):

class SomeClass<T>
{
    /* ... other methods ... */

    private Map<T,T> aSet;

    public T filter(Object value)
    {
        // Will return the properly-typed object if it's in
        // the "set" otherwise will return null
        return aSet.get(value);
    }
}

答案 5 :(得分:1)

这是你的意思吗?

public class SomeClass<T>
{
    private Set<T> aSet;

    public T filter(Object value)
    {
        return (aSet.contains(value) ? (T) value : null);
    }
}

答案 6 :(得分:0)

这样做的问题在于它是等待发生的ClassCastException。如果你的对象不属于T类,但是equals对象是你的一个元素,那么当你尝试将它强制转换为T时,它将会失败。你需要的是获取集合中包含的实际值,你知道它是T类型。不幸的是,Set没有get方法,所以你需要暂时将它转换为具有类似的方法的集合。列表。

您可以尝试这样做:

private Set<T> aSet;

public T filter(Object value) {
    List<T> list = new ArrayList<T>(aSet);
    int index = list.indexOf(value);
    if (index == -1) {
        return null;
    }
    T setValue = list.get(index);
    return setValue;
}

这可能会降低性能,但它提供了更多的类型安全性。