繁体   English   中英

泛型迭代器<e>当集合的原始类型 object 传递给接受通用参数的方法时,行为不同</e>

[英]Generic Iterator<E> behaves differently when a Raw Type of Collection object is passed to method which accepts Generic parameter

  • 当调用 m1() 时,output 会打印 arraylist 的所有元素。
  • 当调用 m2() 时,获取 ClassCastException 说:线程“主”中的异常 java.lang.ClassCastException:test.Employee 无法转换为 java.lang.String。
  public class Stage2Clone {
          public static void main(String[] args) {
              ArrayList stringList = new ArrayList();
              stringList.add(new Employee(1,"A"));
              stringList.add(new Employee(2,"j"));
              stringList.add(new Employee(3,"d"));
              stringList.add("Hello");
              stringList.add(new String("Abc"));
              stringList.add(10);
              stringList.add(new Integer(100));

              System.out.println(stringList);

             m1(stringList);
             m2(stringList);

          }

          public static void m1(ArrayList<Employee> al){
              Iterator<Employee> iterator = al.iterator();
              while(iterator.hasNext()){
                  System.out.println(iterator.next());
              }
          }

          public static void m2(ArrayList<String> al){
              Iterator<String> iterator = al.iterator();
              while(iterator.hasNext()){
                  System.out.println(iterator.next());
              }
          }
      }

那不是迭代器问题,而是破坏代码的 System.out.println。 在运行时,collections 中没有类型检查(有关详细信息,请查找 generics 的类型擦除)

您的代码失败,因为实际上在 m1 中您正在调用

System.out.println(Object ob)

所以这一切都会被打印出来,因为你收藏中的每一件事都是Object

但是在 m2 调用System.out.println(String str)并且这是执行的类型检查,您不能将String以外的 object 传递给此方法

我想向您展示如何防止将来再次发生此类错误

简短的回答

ArrayList 是一个通用 class 不应该以非通用方式创建,只需更改

这一行:

ArrayList stringList = new ArrayList();

对此:

ArrayList<String> stringList = new ArrayList<>();

之后 java 编译器不允许你做这样的坏事。

长答案:

首先让我们讨论一下为什么做这样的事情是非常糟糕的方法。 我应该说这违反了SOLID原则。 SOLID中的主要原则是liskov替换原则。 您可以在此处阅读有关此原理的更多信息,但长话短说,当且仅当父 class 的所有属性都保存时,您可以将子类型的 Object 替换为父类型的 object。 但在这里,您将 ArrayList 类型的ArrayList分配给ArrayList<Employee>ArrayList<String>类型的 Object,这显然违反了 liskov-substition 规则。 为了澄清这种情况,让我们在不改变其逻辑的情况下更改您的m1方法。 我只想在您的 class 中定义新变量并将迭代器的下一个值分配给该值,然后将其传递给 prinLn,所以我们将有这样的事情:

public static void m1(ArrayList<Employee> al){
        Iterator<Employee> iterator = al.iterator();
        while(iterator.hasNext()){
            Employee employee = iterator.next();
            System.out.println(employee);
        }
}

我什么也没做,只定义了新变量? 你认为在这种情况下会发生什么? 在这里,一切都失败了,您的 m1 方法将不再起作用。 为什么。 因为我们将 ArrayList 类型的值分配给 ArrayList 列表的值,并且我们的程序的行为不可预测。 所以不要尝试以其他方式更改您的代码,而只能以正确的形式使用 Generics,否则您将在接下来的夜晚做噩梦。 不要将非泛型 Arraylist 分配给泛型,尽管编译器允许您这样做,稍后我将讨论为什么编译器允许您这样做。 这正是您的代码中发生的事情,最重要的是,在编译时无法检测到这种错误,您可以看到在这种情况下发生了运行时错误。 简而言之,在 java 中,您应该以可以在运行时检测到大量错误的方式进行编码。 在上面的简短回答中,我告诉您不要以非通用方式创建通用 class 智能读者应该在这里问的问题是,如果这是坏事,为什么我们被允许以非通用方式创建通用 class It is the decision that have been made by people who were developing java when they introduced Generic in J2SE 5.0 at those time non-generic java classes were used by many developer, for example there was a List class which was not Generic. 为了向后兼容,他们决定以支持旧代码的方式开发 Generics。 所以通过这种方式,您仍然可以以非通用方式创建通用 class,但这样您就破坏了 class 的通用行为。 简而言之,您不应该进行此实例化。

暂无
暂无

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

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