Question

I created list
private final List<Double> prices
and I have method:

public Double getPrice(int i)
{
    i--;
    if (i >= this.prices.size())
    {
        return Double.NEGATIVE_INFINITY;
    }
    return this.prices.get(i);
}

And looks good... but that always throws:
ClassCastException: java.lang.Integer cannot be cast to java.lang.Double


After some tests I found:
I creating this list from config, but in config I using numbers without dots so code creating list with Integers not Doubles (ArrayList with Integers inside)
But why I can cast this list to List without any error?
Example:

Integer a = 1;
Integer b = 1;
Integer c = 1;
Integer d = 1;
List<?> listI = new ArrayList<>(Arrays.asList(a, b, c, d));
List<Double> listD = (List<Double>) listI;
for (Object o : listD)
{
        System.out.println(o.getClass().getSimpleName()); // Integer
}
System.out.println("done");

This code works without any errors, but printing Integer from Double list, why that don't throw ClassCastException in List<Double> listD = (List<Double>) listI;?
So if you try add double num = listD.get(0); then throw ClassCastException

Was it helpful?

Solution

The generic type parameter that Java infers from the call to Arrays.asList is the common type of the arguments to asList -- Integer, so the new ArrayList is an ArrayList<Integer>, not an ArrayList<Double>.

Java won't throw a ClassCastException at List<Double> listD = (List<Double>) listI;, because at runtime, type erasure has already happened, and to the JVM, the code looks like this:

List listD = (List) listI;

That is perfectly fine to the JVM.

The ClassCastException comes later, from attempting to cast the Integer that is really coming from casting the list to a Double. With generics, the Java compiler will insert an implicit cast to the generic type parameter, in this case, Double, when an item is retrieved. However, it's really an Integer.

There should have been a warning, on the cast when you compiled this code, about an "unchecked cast" when casting the List<?> to a List<Double>. It warns you about the very possibility that you've demonstrated - a potential ClassCastException later.

OTHER TIPS

why that don't throw ClassCastException in List<Double> listD = (List<Double>) listI;?

At run-time all the type information is lost. You have mixed raw type with parametrized type that's why you are getting into trouble at runtime.

Try this one

    List<Integer> listI = new ArrayList<Integer>(Arrays.asList(a, b, c, d));
    List<Double> listD = (List<Double>) listI; // compilation error
                                // Cannot cast from List<Integer> to List<Double>

Sample code :

    // Here's a basic parameterized list.
List<Integer> li = new ArrayList<Integer>();

    // It is legal to assign a parameterized type to a nonparameterized variable
List l = li;

    // This line is a bug, but it compiles and runs.
    // The Java 5.0 compiler will issue an unchecked warning about it.
    // If it appeared as part of a legacy class compiled with Java 1.4, however,
    // then we'd never even get the warning.
l.add("hello");

    // This line compiles without warning but throws ClassCastException at runtime.
    // Note that the failure can occur far away from the actual bug.
Integer i = li.get(0);

For more sample code have a look at Generic Types, Part 1 - O'Reilly Media

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top