5

I have the following two lines:

Map<String,Object>[] IEXDivMap = null;       

IEXDivMap =  new Map[IEXJsonArray.length()];

and get the warning:

The expression of type Map[] needs unchecked conversion to conform to Map<String,Object>[]

Is there a way to fix that?

UPDATE:

I was asked in the comments why we need a Map array to begin with. We are getting a series of hashmaps and placing each one inside the map array. Here's the code:

@SuppressWarnings("unchecked")
public static Map<String,Object>[] getDiv(String ticker) {
    Map<String,Object>[] IEXDivMap = null;
    try{
        String url = "https://api.IEXtrading.com/1.0/stock/" + ticker + "/dividends/1y"; 
        URL obj = new URL(url);   

        HttpURLConnection con = (HttpURLConnection) obj.openConnection();       
        con.setRequestMethod("GET");
        int responseCode = con.getResponseCode();
        if(responseCode == 404){
            System.out.println("Ticker " + ticker + " NOT FOUND in getDiv()!");
            return IEXDivMap;                
        }else if (responseCode != 200){
            System.out.println("IEX Printing All Response Header for URL: " + obj.toString() + "\n");
            Map<String, List<String>> map = con.getHeaderFields();            
            for(Map.Entry<String, List<String>> entry : map.entrySet()) {
                System.out.println("IEX " + entry.getKey() + " : " + entry.getValue());
            }
        }

        BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
        String inputLine;
        StringBuilder response = new StringBuilder();

        while ((inputLine = in.readLine()) != null) {
            response.append(inputLine);             
        }
        in.close();

        JSONArray IEXJsonArray = new JSONArray(response.toString());        
        IEXDivMap = new Map[IEXJsonArray.length()];

        for (int i = 0; i < IEXJsonArray.length(); i++) {
            IEXDivMap[i] = new HashMap<String,Object>();
            JSONObject IEXJsonObject = IEXJsonArray.getJSONObject(i);

            IEXDivMap[i].put("exDate",IEXJsonObject.getString("exDate"));
            IEXDivMap[i].put("amount",IEXJsonObject.getString("amount"));

            //System.out.println(IEXDivMap[i]);
            System.out.println(IEXDivMap[i].get("exDate") + " 0  " + IEXDivMap[i].get("amount"));
        }
    }catch(Exception e){
        System.out.println("FATAL ERROR: Something went wrong in getDiv " + e.getMessage());
        System.exit(0);
    }
    return IEXDivMap; 
}
DCR
  • 10,658
  • 7
  • 38
  • 86
  • could you explain? – DCR Dec 29 '18 at 20:31
  • 2
    Why are you doing this at all? Generic arrays are *heavily* discouraged. – Makoto Dec 29 '18 at 20:48
  • 1
    @AndrewTobiliko: Not 100% sold that this is a duplicate since there's an XY problem going on here. The OP is using arrays to solve their problem but the real question is why the arrays are there at all. – Makoto Dec 29 '18 at 20:52
  • In addition to your question at hand, may I recommend to follow the Java naming conventions where variable names should begin with lowercase letters? Your colleages will be grateful. – Ralf Kleberhoff Dec 29 '18 at 22:15
  • @Makoto updated the question. Is there a better way? – DCR Dec 30 '18 at 02:25

5 Answers5

7

Unfortunately it's not possible to fix the issue in a clean way.

The clean solution would be to create generic array, but it is not possible due to type erasure.

Why generic array creation is forbidden?

Consider the following example with arrays:

Object[] arr = new String[1];
arr[0] = 10;

The result is ArrayStoreException.

Now imagine that generic array creation is allowed:

Map<String, String>[] map = new Map<>[1]; // This is illegal
Object[] objects = map;
objects[0] = new HashMap<Integer, Integer>(); // No ArrayStoreException

The compiler warns that one could put any Map to the array and neither compiler, nor runtime can check it. Hence the warning

Workarounds?

Quoting Java generics faq you can use:

  • array of raw type
  • array of unbounded wildcard parameterized type
  • collection instead of array

Personally I would strongly recommend you to consider using a List:

List<Map<String, Object>> list = new ArrayList<>();
Denis Zavedeev
  • 5,335
  • 4
  • 23
  • 39
  • Although this answer contains some very good points, I don't think it exactly matches the original question, as there is no step intended like your `Object[] objects = map;`. It's a pity that the array type system compiles such a statement instead of flagging an error, but you don't need generics to see that flaw. – Ralf Kleberhoff Dec 29 '18 at 22:12
  • @RalfKleberhoff you're right. OP has no intention to do something like `Object[] objects = map;`. But here I was trying to explain why the warning is generated at all (How can I do it better?) – Denis Zavedeev Dec 30 '18 at 11:50
2

There's no way you can avoid that warning. Array creation only allows the use of raw types as element type. I'm sure there's a reason why the Java experts decided not to allow generics there, but I don't know that reason.

So if you really want an array of Map<String,Object> elements (and for some reason can't use a List instead), you can declare a variable with that generic type, but you'll always get that warning when storing a freshly-created array, as that can only have a raw type.

But it's only a warning, and your code will be running perfectly fine. It's a warning and not an error, meaning that there's something that needs the developer's attention as the compiler can't decide whether it's ok or not.

So what's the problem causing the warning?

At runtime a Map<String,Object>[] is just a Map[], as generic types are erased during compilation. So the array creation produces the correct runtime type of array, and you won't get a ClassCastException from that assignment. Checking for the generics rules is done only at compile-time: it's the compiler's duty to make sure that the runtime-Map[] that gets assigned to a Map<String,Object>[] variable only contains Maps where Strings are used as keys and Objects as values (and of course null values are also acceptable as array elements).

Now if you assign a compile-time Map[] value to a Map<String,Object>[] variable,

Map[] rawArray = ...;
Map<String,Object>[] generic Array = rawArray;

the compiler doesn't know what types of keys and values the maps contained in rawArray use, so he rightfully warns you of this type-incompatibility risk. But in the case of a freshly created new Map[n],

Map<String,Object>[] genericArray = new Map[5];

the compiler simply isn't intelligent enough to see that this array only contains its initial null values, and thus perfectly conforms to the Map<String,Object>[] requirements. This statement doesn't really contain any type-related risk, it's just the compiler not being intelligent enough to understand.

Hence my recommendation is to either ignore the warning or to place an appropriate @SuppressWarnings annotation there, commenting why it's justified to suppress the warning. It's a pity that we can't apply the @SuppressWarnings annotation to an individual statement, so you might want to refactor the array creation into a method of its own, so the @SuppressWarnings annotation applies only to that action and not to your whole method.

Ralf Kleberhoff
  • 5,711
  • 1
  • 8
  • 6
0

I'm not seeing the inherent benefit of using an array over a collection like a List for your use case. Simply change your code to use a List instead, which will guarantee type safety and eliminate the need to do any kind of indexing or the like.

The big things that'll change:

  • How you declare your return type

    public static List<Map<String,Object>> getDiv(String ticker) 
    
  • How you instantiate the holder

    List<Map<String,Object>> IEXDivMap = new ArrayList<>();
    
  • How you fill the map (kind of, you just change how you're referencing it)

    Map<String, Object> divMap = new HashMap<>();
    
Makoto
  • 96,408
  • 24
  • 164
  • 210
0

It's not possible to avoid a warning.

You can avoid an unchecked conversion warning by explicitly casting it into the proper parameterized array type:

IEXDivMap = (Map<String, Object>[]) new Map[IEXJsonArray.length()];

or

IEXDivMap = (Map<String, Object>[]) new Map<?,?>[IEXJsonArray.length()];

but then it will cause an unchecked cast warning instead, so you are trading one warning for another.

newacct
  • 110,405
  • 27
  • 152
  • 217
-2

Maybe like this?

import java.util.Map;
import java.util.HashMap;

public class test {
  public static void main(String[] args) {
    class MyMap extends HashMap<String,Object> { }
    MyMap[] IEXDivMap = null;       

    IEXDivMap = new MyMap[2];
  }
}
rado
  • 3,830
  • 3
  • 28
  • 24
  • 2
    Is this an answer or a guess? What does this code do and why does it solve the problem? – JJJ Dec 30 '18 at 12:37