类型安全:从 Object 到 List<MyObject> 的未经检查的强制转换

Posted

技术标签:

【中文标题】类型安全:从 Object 到 List<MyObject> 的未经检查的强制转换【英文标题】:Type safety: Unchecked cast from Object to List<MyObject> 【发布时间】:2022-02-03 10:43:38 【问题描述】:

我有一个 ListView 列出了一个自定义对象(比如说MyObject)。

我想通过 EditText 动态过滤它,所以我必须使用 publishResults 方法实现 getFilter()

@Override
protected void publishResults(CharSequence constraint, FilterResults results) 
    MyObjectAdapter.this.setItems((List<MyObject>) results.values);
    MyObjectAdapter.this.notifyDataSetChanged();

此时,Eclipse 报错:Type safety: Unchecked cast from Object to List&lt;MyObject&gt;

我确信这个演员表永远是正确的,但 Eclipse 只建议添加@SuppressWarnings("unchecked"),但我完全反对SuppressWarnings,因为它只是隐藏问题,而不是解决方案......

我尝试添加:

if(results.values instanceof List<MyObject>)

但是 Eclipse 再次抱怨,这并没有解决任何问题......

Cannot perform instanceof check against parameterized type List&lt;MyObject&gt;. Use the form List&lt;?&gt;

我知道强制转换总是正确的,但这是确保results.values 实际上是List&lt;MyObject&gt; 的正确方法?

提前致谢!

【问题讨论】:

***.com/questions/262367/type-safety-unchecked-cast 【参考方案1】:

如果您只需要使用Object,那么您就无法在运行时检查您实际上是否拥有List&lt;MyObject&gt;,因为泛型类型MyObject 仅用于编译时类型检查,它在运行时可用。这就是您尝试添加instanceof 检查时收到错误的原因。

如果您确定您的 Object 确实始终是 List&lt;MyObject&gt;,那么我会说 @SuppressWarnings 没问题,如果您记录了为什么您确定这不是问题。

如果您绝对想避免警告,您可以创建自己的List 实现(例如MyObjectList),它本身不是通用的,但实现了List&lt;MyObject&gt;。然后您可以在运行时对MyObjectList 进行instanceof 检查。

另一个选项是检查 List&lt;?&gt; 并将其转换为 instanceof 错误提示。然后您可以遍历列表中的元素并检查它们是否实际上都是 MyObject 的所有实例,并将它们复制到新的List&lt;MyObject&gt;

【讨论】:

【参考方案2】:

嗯,我终于找到了解决办法。

正如@Medo42 所说:

另一种选择是检查并转换为 List 作为 instanceof 错误提示。然后您可以遍历列表中的元素并 检查它们是否实际上是 MyObject 的所有实例,并将它们复制到 一个新的列表。

尽管我没有经历过创建一个全新对象的过程以使这个特殊情况“无警告”地工作,但这是正确的方向。

所以我采用了@lokoko 的想法并将其用于新的setItems() 方法,使用Object 参数而不是List&lt;MyObject&gt; 以确保

结果代码如下:

public void setItems(List<MyObject> var)
    this.list = var;


public void setItems(Object var)
    List<MyObject> result = new ArrayList<MyObject>();
    if (var instanceof List)
        for(int i = 0; i < ((List<?>)var).size(); i++)
            Object item = ((List<?>) var).get(i);
            if(item instanceof MyObject)
                result.add((MyObject) item);
            
        
    
    setItems(result);

感谢大家的帮助!

【讨论】:

【参考方案3】:

试试这样的:

List<?> result = (List<?>) results.values;
for (Object object : result) 
    if (object instanceof MyObject) 
        tempList.add((MyObject) object); // <-- add to temp
    


filteredItems = tempList; // <-- set filtered

【讨论】:

您的解决方案帮助我弄清楚如何解决我的问题,谢谢!【参考方案4】:

您可以在将其传递给setItems()之前执行检查。

final Object myListObj = reuslts.values;
if(myListObj instanceof List<?>) 
    if(((List<?>)myListObj).get(0) instanceof MyObject)
        // You can safely suppress the warning here because you made sure it is a List containing MyObject
        MyObjectAdapter.this.setItems((List<? extends MyObject>) myListObj);


但是,您需要相应地更改您的 setItems() 方法:

public void setItems(List<? extends MyObject> list) 
    // Your code here

【讨论】:

这个检查只确保列表中的第一个对象是MyObject,所以你唯一显示的是列表可以 包含这样的元素 - 并不是它实际上是 List&lt;MyObject&gt; 甚至是 List&lt;? extends MyObject&gt;。相反,您已经证明它是List&lt;? super MyObject&gt; 你是对的。但是,List&lt;String&gt; 永远不能包含Integer 的元素,对吧?如果MyObject 没有子类,这很好用。即使extends MyObject中的对象,它们都包含MyObjectAdapter所需的必要接口或函数,因为方法签名声明了MyObject,而不是其他子类。 从技术上讲,List&lt;String&gt;可以包含Integer,因为在运行时不强制执行列表的泛型类型。但即使你有这样的保证,即使MyObject 没有子类,你的建议也是无效的。想象一个List&lt;Object&gt; 包含一个MyObject 和一个字符串。您的检查会将其转换为List&lt;? extends MyObject&gt;,因为第一个元素恰好是MyObject,但试图通过该转换列表获取第二个元素会导致ClassCastException

以上是关于类型安全:从 Object 到 List<MyObject> 的未经检查的强制转换的主要内容,如果未能解决你的问题,请参考以下文章

某些 Java 泛型类型转换中的类型安全警告是啥意思?

怎样从jsp页面传递一个List<Map<String,Object>>的集合到Action

ArrayList和List主要区别 就是ArrayList类型不安全。

为啥我不能从 List<MyClass> 转换为 List<object>?

我试图从 webmethod 返回 list<object> 但给出错误

Hashmap,Set,Map,List,ArrayList的区别