【问题标题】:Type safety: Unchecked cast from Object to List<MyObject>类型安全:从 Object 到 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; 的正确方法?

提前致谢!

【问题讨论】:

标签: android listview casting filter custom-object


【解决方案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
      猜你喜欢
      • 1970-01-01
      • 2013-12-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多