Home > Mobile >  How does type-casting fail but work fine after constructor
How does type-casting fail but work fine after constructor

Time:11-09

I am exploring Java and while trying type-casting I ran into the following issue, the casting failed when I called the method the first time but after constructing the List it worked fine.

Looking for a clear explanation to this please :) Thanks

import java.util.*;
import java.util.Set;
import java.util.HashMap; // import the HashMap class


public class Main{     
    public static void main(String[] args)     
    {     
        Set<Map<String, ?>> rows = new HashSet();
        
        HashMap<String, String> map = new HashMap<String, String>();
        map.put("1","one");
        map.put("2","two");
        
        HashMap<String, String> mapp = new HashMap<String, String>();
        mapp.put("3","three");
        mapp.put("4","four");
        
        rows.add(map);
        rows.add(mapp);

        //WHY THE FOLLOWING DOESN'T WORK ?????
        //printItems((List<Map<String, ?>>) rows);
        
        //BUT THE FOLLOWING WORKS FINE
        List<Map<String, ?>> listedRows = new ArrayList<>(rows);
        printItems(listedRows);
    }
    
    public static void printItems(List<Map<String, ?>> items) {
        for (Map<String, ?> str: items)
            System.out.println(str);
    }
}    

CodePudding user response:

It doesn't work because the run-time type of rows is HashSet and HashSet does not implement the List interface.

It works when you create listedRows because the run-time type of that object is ArrayList which does implement the List interface.

In your example, you could just just use the Collection abstraction in printItems since all you need to do is iterate through items. This will allow you to invoke the method with a Set or List (or any other Collection) without having to cast or recreate an object.

    public static void printItems(Collection<Map<String, ?>> items) {
        for (Map<String, ?> str: items)
            System.out.println(str);
    }

On casting if you are interested:

Casting generally only works when you have an object with a compile-time type that is a parent class of the run-time type and you need to coerce the compile-time type of the object to be the run-time type so that you can invoke a method that is specific to the run-time type.

For example (albeit contrived):

public void addTenObjects(List l) {

  if (l instanceof ArrayList)
    ((ArrayList)l).ensureCapacity(10)
  
  for (int i = 0; i < 10; i  ) 
    l.add(new Object())
}

CodePudding user response:

Both Set and List interfaces extend Collection, but they don't extend each other. I.e. they are sibling, therefore you can't convert one into another via casting.

Since your method printItems simply printing the contents of the argument passed into it, you can use instead of it Java 8 Iterable.forEach(), which expects a Consumer representing the action:

rows.forEach(System.out::println);

No need to type-cast or copy anything, and no need in introducing the method when you have a fairly simple logic like in this case.

  • Related