从Object,泛型类和通配符中取消选中

时间:2013-10-01 15:14:41

标签: java generics wildcard

这是我的代码:

public class ArrayTaskList<E> {
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        ArrayTaskList<E> other = (ArrayTaskList<E>) obj;
        if (!Arrays.equals(db, other.db))
            return false;
        return true;
    }
}

编译说:

  

类型安全:从对象到arraytasklist

的未选中强制转换

我理解,这是一个警告,但是如果我尝试这个代码,就没有警告:

ArrayTaskList<?> other = (ArrayTaskList<?>) obj;

解决方案更方便吗?

4 个答案:

答案 0 :(得分:3)

区别在于原始类型对象不是类型安全的,而无界通配符为您提供类型安全。

例如,对于原始类型,您可以使用以下代码:

List list = new ArrayList<String>();
list.add(42); // integer
list.add(true); // boolean
list.add(whateverYouWant); // whatever you want

这段代码:

List<?> list2 = new ArrayList<String>();
list2.add(42);
list2.add(true);

会导致编译错误。

答案 1 :(得分:0)

当执行到达该行时,您知道objArrayTaskList的实例。 但是,您不知道它是ArrayTaskList<Integer>还是ArrayTaskList<String>等。

因此,演员阵容会产生警告(您可以尝试将ArrayTaskList<Integer>投射到ArrayTaskList<String>)。

但是,您不需要此处的类型信息,因此使用ArrayTaskList<?>确实是更好的解决方案。

修改

我在这里有一些误解,因为即使使用类型的边界也会引起警告。 正如@svz指出的那样,ArrayTaskList<?>不会添加任何假设,只会启用类型检查。

编译器相信转换为ArrayTaskList是正常的,而生成警告是因为您假设obj也具有类型E。编译器无法检查,因此发出警告。对于<?><? extends XYZ>,编译器将忽略该类型,但如果调用可能失败的任何方法,则会引发错误。

考虑以下示例:

您的课程为ArrayTaskList<E extends Number>,因此您可以投放到ArrayTaskList<Number>ArrayTaskList<E>(例如E可能是Long)。

在这种情况下,编译器不知道ENumberLong等类型,因此警告你,因为obj可能是ArrayTaskList<Double> 1}}并将其转换为ArrayTaskList<Number>将允许您将Longs添加到双打(ouch)列表中。

因此编译器会警告你那个演员。

转换为ArrayTaskList<?>会告诉编译器忽略该类型,但如果您调用other.add(...),则会引发错误,从而阻止您出现意外不一致。

编辑2:

我仍然有一些误解(我会更多地考虑这个),但到目前为止,这是一种在没有警告的情况下投射的方法,仍然使用E可能提供的任何上限:

public boolean equals(Object obj) {
  ...      
  return equals_((ArrayTaskList<?>)obj);
}

protected boolean equals_(ArrayTaskList<? extends Number> other)
{      
  if (!Arrays.equals(db, other.db))
        return false;
  return true;
}

答案 2 :(得分:0)

您还可以向ArrayTaskList构造函数传递Class<E> clazz的内容,然后可以完成演员clazz.cast(...)

public class ArrayTaskList<E> {
    Class<ArrayTaskList<E>> clazz;

    public ArrayTaskList(Class<ArrayTaskList<E>> c) {
        clazz = c;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        ArrayTaskList<E> other = clazz.cast(obj);
        if (!Arrays.equals(db, other.db))
            return false;
        return true;
    }
}

答案 3 :(得分:0)

您可以改用以下实现:

@Override
public boolean equals(Object obj) {
    return obj instanceof ArrayTaskList && obj.hashCode() == hashCode();
}

@Override
public int hashCode() {
    return Arrays.hashCode(db);
}

这样,就不再有未经检查的演员问题;)

但请注意,由于类型擦除,

new ArrayTaskList<String>().equals(new ArrayTaskList<Integer>())
如果两者都具有相同的true数组,

将返回db,即使一个使用String而另一个Integer作为类参数。