简体   繁体   中英

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.
  • When m2() is called get ClassCastException saying: Exception in thread "main" java.lang.ClassCastException: test.Employee cannot be cast to 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. On runtime there are no type checkings in collections (lookup Type Erasure for generics for details)

Your code fails because in m1 you are calling in fact

System.out.println(Object ob)

so it all gets printed as every thing in your collection is an 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

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

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.

Long answer:

First lets discuss why doing something like that is very bad approach. I should say that it is against SOLID principle. and the Main Principle in SOLID is liskov substitution principle. 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. 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. for clatifying this situation lets change your m1 method without changing its logic. 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:

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. why. because we assign a value of type ArrayList to value of ArrayList List and our program behave unpredictably. 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. 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. 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. 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 . 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. 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. in a nutshull you shouldn't do this instantiation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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