简体   繁体   中英

Why does this method allow a double to be stored in ArrayList of type Integer?

I have a method that is supposed to return an instance of a given collection class with the given entries. The implementation is shown below.

public static <E, C extends Collection<E>> C
         initalizeAndAddToCollection(Class<C> clazz, E... entries) {
    //Collection object
    Collection<E> collection;
    //try to invoke default constructor of C
    try {
        collection = clazz.getDeclaredConstructor().newInstance();
    } catch (InstantiationException | IllegalAccessException |
             InvocationTargetException | NoSuchMethodException e) {
        throw new RuntimeException();
    }
    //Add elements to collection
    for (E entry: entries)
        collection.add(entry);
    return (C) collection;
}

The problem is that the following code runs even though a double should not be able to be stored in a list of type Integer

 //Create and instantiate an ArrayList with element 1.0
 ArrayList<Integer> list = initalizeAndAddToCollection(ArrayList.class, 1.0);
 System.out.print(list.get(0));

Why does this code run, and how do I make it so that it results in either a compile or runtime error?

Edit: I noticed list.get(0).getClass() does generate an exception, but I am not sure why either (or why the previous code does not).

In Java, generics are a compile-time only feature because they're implemented via type erasure . So while you may think you're creating an ArrayList<Integer> at runtime with your newInstance call, you're really just creating an ArrayList .

In short, reflection breaks type safety for generics in Java.

This code gives a warning when I compile it. The warning says:

Note: MyTest.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

And in fact, when you do what it produces 4 warning messages:

MyTest.java:5: warning: [unchecked] Possible heap pollution from parameterized vararg type E
        initalizeAndAddToCollection(Class<C> clazz, E... entries) {
                                             ^
  where E,C are type-variables:
    E extends Object declared in method <E,C>initalizeAndAddToCollection(Class<C>,E...)
    C extends Collection<E> declared in method <E,C>initalizeAndAddToCollection(Class<C>,E...)


MyTest.java:18: warning: [unchecked] unchecked cast
        return (C) collection;
                   ^
  required: C
  found:    Collection<E>
  where E,C are type-variables:
    E extends Object declared in method <E,C>initalizeAndAddToCollection(Class<C>,E...)
    C extends Collection<E> declared in method <E,C>initalizeAndAddToCollection(Class<C>,E...)
MyTest.java:23: warning: [unchecked] unchecked method invocation: method initalizeAndAddToCollection in class MyTest is applied to given types
             initalizeAndAddToCollection(ArrayList.class, 1.0);
                                        ^
  required: Class<C>,E[]
  found: Class<ArrayList>,double
  where C,E are type-variables:
    C extends Collection<E> declared in method <E,C>initalizeAndAddToCollection(Class<C>,E...)
    E extends Object declared in method <E,C>initalizeAndAddToCollection(Class<C>,E...)
MyTest.java:23: warning: [unchecked] unchecked conversion
             initalizeAndAddToCollection(ArrayList.class, 1.0);
                                        ^
  required: ArrayList<Integer>
  found:    ArrayList
4 warnings

These warnings explain why this seemingly incorrect code is compiling. You are doing things that the compiler would tell you are incorrect.


Why does this code run.

Because it is not actually breaking runtime type-safety.

  • Type erasure means that the collection is actually storing references to Object .

  • When you then do System.out.print(list.get(0)); the parameter type for the print call is Object . That means no implicit cast to Integer is needed.

and how do I make it so that so that it results in either a compile or runtime error?

If you need a compile time error, tell the compiler to treat warnings as errors. (Or check for warnings in the compilation output.)

If you need a runtime error, you need to add some explicit runtime type checks probably in your initalizeAndAddToCollection method.

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