简体   繁体   English

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

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

  • When m1() is called the output prints all the elements of arraylist.当调用 m1() 时,output 会打印 arraylist 的所有元素。
  • When m2() is called get ClassCastException saying: Exception in thread "main" java.lang.ClassCastException: test.Employee cannot be cast to java.lang.String.当调用 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());
              }
          }
      }

Thats not the iterator issue but the System.out.println that breaks the code.那不是迭代器问题,而是破坏代码的 System.out.println。 On runtime there are no type checkings in collections (lookup Type Erasure for generics for details)在运行时,collections 中没有类型检查(有关详细信息,请查找 generics 的类型擦除)

Your code fails because in m1 you are calling in fact您的代码失败,因为实际上在 m1 中您正在调用

System.out.println(Object ob)

so it all gets printed as every thing in your collection is an Object所以这一切都会被打印出来,因为你收藏中的每一件事都是Object

but in the m2 calls System.out.println(String str) and here is the type check performed, you cannot pass object other than String to this method但是在 m2 调用System.out.println(String str)并且这是执行的类型检查,您不能将String以外的 object 传递给此方法

I want to show you what to do to prevent such Error happen again in the future我想向您展示如何防止将来再次发生此类错误

Short answer :简短的回答

ArrayList is a Generic class and it should not be created in non-generic way, just change ArrayList 是一个通用 class 不应该以非通用方式创建,只需更改

this line:这一行:

ArrayList stringList = new ArrayList();

to this one:对此:

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

And afterward java compiler does not allow you to do something bad like this.之后 java 编译器不允许你做这样的坏事。

Long answer:长答案:

First lets discuss why doing something like that is very bad approach.首先让我们讨论一下为什么做这样的事情是非常糟糕的方法。 I should say that it is against SOLID principle.我应该说这违反了SOLID原则。 and the Main Principle in SOLID is liskov substitution principle. SOLID中的主要原则是liskov替换原则。 you can read more about this principle here but Long story short It says that you can replace Object of subtype to object of parent type if and only if all properties of parent class saves.您可以在此处阅读有关此原理的更多信息,但长话短说,当且仅当父 class 的所有属性都保存时,您可以将子类型的 Object 替换为父类型的 object。 but here you are assigning Object of type ArrayList to Object of type ArrayList<Employee> and ArrayList<String> which is clearly against liskov-substition rule.但在这里,您将 ArrayList 类型的ArrayList分配给ArrayList<Employee>ArrayList<String>类型的 Object,这显然违反了 liskov-substition 规则。 for clatifying this situation lets change your m1 method without changing its logic.为了澄清这种情况,让我们在不改变其逻辑的情况下更改您的m1方法。 I only want define new variable in your class and assign the next value of the iterator to that value then pass it to prinLn, so we will have something like this:我只想在您的 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);
        }
}

I didn't do anything but only defining new variable?我什么也没做,只定义了新变量? What do you think will happen in this situation?.你认为在这种情况下会发生什么? here all hell broke lose and your m1 method won't work anymore.在这里,一切都失败了,您的 m1 方法将不再起作用。 why.为什么。 because we assign a value of type ArrayList to value of ArrayList List and our program behave unpredictably.因为我们将 ArrayList 类型的值分配给 ArrayList 列表的值,并且我们的程序的行为不可预测。 so don't try to change your code in other way but only use Generics in correct form otherwise you will have nightmare in following nights.所以不要尝试以其他方式更改您的代码,而只能以正确的形式使用 Generics,否则您将在接下来的夜晚做噩梦。 don't assign non-Generics Arraylist to Generic one although compiler let you to do that I will discuss why compiler let you doing that later.不要将非泛型 Arraylist 分配给泛型,尽管编译器允许您这样做,稍后我将讨论为什么编译器允许您这样做。 It is exactly what is happening in your code and on top of that this kind of error can not be detected at compile time and you can see that Runtime error happened in this situation.这正是您的代码中发生的事情,最重要的是,在编译时无法检测到这种错误,您可以看到在这种情况下发生了运行时错误。 In a nutshell in java you should code in a way that large amount of error can be detected at runtime.简而言之,在 java 中,您应该以可以在运行时检测到大量错误的方式进行编码。 In short answer above I told you not to create Generic class in non-generic-way the question that intelligent reader should ask here is that if it is something bad why we are allowed to create Generic class in non-generic way .在上面的简短回答中,我告诉您不要以非通用方式创建通用 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. 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. For backward compatibility they decided to develop Generics in a way that support old legcy code.为了向后兼容,他们决定以支持旧代码的方式开发 Generics。 so in this way you can still create Generic class in non-generic way but this way you are breacking the generic behaviour of your class.所以通过这种方式,您仍然可以以非通用方式创建通用 class,但这样您就破坏了 class 的通用行为。 in a nutshull you shouldn't do this instantiation.简而言之,您不应该进行此实例化。

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

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