javaarraysjava-streamparameterized-types

java stream return array of parameterized class


Situation:

public class P {

    public static  Predicate<Double> isEqual(double value) {
        return p -> (Math.abs(p - value) <= 0.000001);
    }
}

public class Entity {   
    private double[] values;

    public double getValue(int index) {
        return values[index];
    }
}

Code with unchecked conversion:

public Attribute split(Entity[] examples) {
    @SuppressWarnings("unchecked") 
    Predicate<Double>[] pa = Arrays.stream(examples).map(e -> P.isEqual(e.getValue(a.index))).toArray(Predicate[]::new);
    return ...;
}

How can solve this without unchecked conversion?

I can't use such:

public Attribute split(Entity[] examples) {
    Predicate<Double>[] pa = Arrays.stream(examples).map(e -> P.isEqual(e.getValue(a.index))).toArray(Predicate<Double>[]::new);
    return ...;
}

Solution

  • There is no way to create a generic array without unchecked conversion, as the creation of a generic array is an unsafe operation per se. Since its creation is not allowed, you can only create a non-generic array and perform an unchecked conversion.

    Simply said, every operation that could enable subsequent heap pollution without any warning, must be considered an unsafe operation that either, generates a warning itself or even will produce an error. The problem can easily demonstrated:

    Predicate<Double>[] p=/* someway to get the array*/;
    Object[] array=p;
    array[0]=(Predicate<Integer>)i -> i==0;
    

    This situation, where an array whose elements are declared to be of type Predicate<Double>, but one of them is actually a Predicate<Integer>, is called heap pollution. Since both, the assignment of p to a variable of type Object[] and the assignment of a Predicate<Integer> to an element of an array of type Object[], are legal constructs that don’t generate warnings, the creation of the array itself must be considered an unsafe operation that must generate either, a warning or an error, to prevent the subsequent silent heap pollution. Otherwise, Generics could not claim to provide compile-time safety, i.e. that code free of unchecked/unsafe operations guarantees the absence of heap pollution.

    The only exception where generic array creation is accepted, is in the context of varargs, but to have a warning free code, i.e. using @SafeVarargs, you have to accept fundamental restrictions, e.g. you can not assign the array to any other variable nor return it from the method, to avoid a sneaky introduction of the problem described above.

    So the bottom line is, either, you accept that there is an unchecked operation or you use a List and “use a List” means use a List, not try to create the array via a List. The fundamental difference between a List and an array is that you can’t assign a List<Predicate<Double>> to a List<Predicate> nor List<Object> (without an unchecked operation), so it provides the safety that Generics promise.