简体   繁体   English

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

[英]Unchecked cast from Object, generic class and wildcards

Here is my code: 这是我的代码:

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;
    }
}

And compiler says: 编译器说:

Type safety: Unchecked cast from object to arraytasklist 类型安全:从对象到arraytasklist的未选中强制转换

I understand, that it's jst an warning, but if i try this code, there are no warnings: 我明白,这是一个警告,但如果我尝试这个代码,没有警告:

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

Is it more convenient solution? 这是更方便的解决方案吗?

The difference is that the raw type object is not typesafe while the unbounded wildcard gives you type safety. 区别在于原始类型对象不是类型安全的,而无界通配符为您提供类型安全性。

For example with a raw type you can have code like this: 例如,对于原始类型,您可以使用以下代码:

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

while this code: 而这段代码:

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

will cause a compiler error. 将导致编译器错误。

When execution reaches that line, you know that obj is an instance of ArrayTaskList . 当执行到达该行时,您知道objArrayTaskList一个实例。 However, you don't know whether it is an ArrayTaskList<Integer> or ArrayTaskList<String> etc. 但是,您不知道它是ArrayTaskList<Integer>还是ArrayTaskList<String>等。

Thus, the cast generates a warning (you could try to cast a ArrayTaskList<Integer> to ArrayTaskList<String> ). 因此, ArrayTaskList<Integer>会生成警告(您可以尝试将ArrayTaskList<Integer> ArrayTaskList<String>ArrayTaskList<String> )。

However, you don't need the type information here, so using ArrayTaskList<?> would indeed be the better solution here. 但是,这里不需要类型信息,因此使用ArrayTaskList<?>确实是更好的解决方案。

EDIT : 编辑

I had some misconception here, since even using type's boundary would cause the warning. 我在这里有一些误解,因为即使使用类型的边界也会引起警告。 As @svz pointed out, ArrayTaskList<?> won't add any assumptions and just enables type checking. 正如@svz所指出的, ArrayTaskList<?>不会添加任何假设,只会启用类型检查。

The compiler trusts you that the cast to ArrayTaskList is ok, while the warning is generated because you make the assumption of obj also having the type E . 编译器信任您对ArrayTaskList是正确的,同时生成警告是因为您假设obj也具有类型E The compiler can't check that and thus issues the warning. 编译器无法检查,因此发出警告。 With <?> or <? extends XYZ> <?><? extends XYZ> <? extends XYZ> the compiler will ignore the type but will raise errors if any method that might fail is invoked. <? extends XYZ>编译器将忽略该类型,但如果调用任何可能失败的方法,则会引发错误。

Consider the following example: 请考虑以下示例:

Your class is ArrayTaskList<E extends Number> and thus you'd cast to ArrayTaskList<Number> or ArrayTaskList<E> (where E could be Long for example). 您的类是ArrayTaskList<E extends Number> ,因此您将转换为ArrayTaskList<Number>ArrayTaskList<E> (例如,其中E可能为Long )。

In that case the compiler doesn't know whether E is of type Number , Long etc. and thus warns you, since obj could be a ArrayTaskList<Double> and casting that to ArrayTaskList<Number> would allow you to add Longs to a list of Doubles (ouch). 在这种情况下,编译器不知道E是否为NumberLong等类型并因此警告您,因为obj可能是ArrayTaskList<Double>并且将其转换为ArrayTaskList<Number>将允许您将Longs添加到列表中双打(哎哟)

Hence the compiler warns you of that cast. 因此编译器警告你那个演员。

Casting to ArrayTaskList<?> would tell the compiler to ignore the type but raise errors if you called other.add(...) , preventing you from accidential inconstencies. 转换为ArrayTaskList<?>会告诉编译器忽略该类型,但如果调用other.add(...)引发错误,从而防止出现意外不一致的情况。

Edit 2: 编辑2:

I still have some misconception here (I'll think more about this), but so far, here's a way to cast without a warning and still use any upper bound that E might provide: 我仍然有一些误解(我会更多地考虑这个),但到目前为止,这是一种没有警告的方法,仍然使用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;
}

You could also pass in the ArrayTaskList constructor an inscance of Class<E> clazz and then the cast can be done clazz.cast(...) : 您还可以在ArrayTaskList构造函数中传入Class<E> clazz的inscance,然后可以使用clazz.cast(...)进行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;
    }
}

You could use the following implementation instead: 您可以使用以下实现:

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

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

That way, there's no unchecked cast problem anymore ;) 这样,就没有任何未经检查的演员问题;)

But please note that due to type erasure, 但请注意,由于类型擦除,

new ArrayTaskList<String>().equals(new ArrayTaskList<Integer>())

will return true if both have the same db array, even if one used String while the other one Integer as class parameter. 如果两者都具有相同的db数组,则返回true ,即使一个使用String而另一个使用Integer作为类参数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM