3

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 ...;
}
Tagir Valeev
  • 87,515
  • 18
  • 194
  • 305
evgenii ershenko
  • 427
  • 5
  • 13
  • 1
    I'll let someone more knowledgeable answer, but my gut feeling is that you *cannot* do this without an unchecked conversion. Type erasure applies to array constructors, so `Predicate[]::new` does not make sense from the compiler's point of view. – Chill Jun 01 '16 at 21:14
  • 1
    I think @Chill is correct. All the standard examples of [how to create a generic array](http://stackoverflow.com/questions/529085/how-to-create-a-generic-array-in-java) that I can find rely on unchecked conversion at one point or another. – azurefrog Jun 01 '16 at 21:16
  • I can also note that something like `Predicate[]::new` is basically equivalent to `Function[]> predicateMaker = (Integer size) -> new Predicate[size];`. The error here is more clear: "Cannot create a generic array of Predicate". Because you cannot create a generic array without an unchecked conversion (as @azurefrog noted in the linked page). – Chill Jun 01 '16 at 21:30
  • 1
    Generally speaking you're better off creating a list, not an array. – Louis Wasserman Jun 01 '16 at 21:58
  • Louis and others, would be it better next? : Predicate[] pa = null; List> lp = Arrays.stream(examples).map(e -> P.isEqual(e.getValue(a.index))).collect(Collectors.toList()); lp.toArray(pa); – evgenii ershenko Jun 01 '16 at 22:36
  • I think best next(order not important): Predicate[] pa = null; List> lp = Arrays.stream(examples).map(e -> P.isEqual(e.getValue(a.index))).collect(ArrayList>::new, ArrayList>::add, ArrayList>::addAll); lp.toArray(pa); – evgenii ershenko Jun 01 '16 at 23:10
  • 1
    You cannot use `toArray(pa)` when `pa` is `null`. That will cause a `NullPointerException`. And pre-allocating that array brings you back to square zero; creation of generic arrays is not supported. – Holger Jun 02 '16 at 14:13

2 Answers2

1

I'm actually going to make my comments an answer because I found this interesting to research and I'm now pretty confident. The answer hinges on the fact (as @azurefrog noted) that creating generic arrays must have an unchecked conversion.

See: How to create a generic array in Java?

Predicate<Double>[]::new would be equivalent to

IntFunction<Predicate<Double> []> predicateArrayMaker = (int size) -> new Predicate<Double>[size];

However, you cannot create a generic array without an unchecked conversion. For example,

Predicate<Double>[] predicateArray = new Predicate<Double>[10];

will have the same problem.

So, the unchecked conversion must necessarily happen as explained in the linked answer.

Community
  • 1
  • 1
Chill
  • 1,063
  • 6
  • 13
1

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.

Holger
  • 243,335
  • 30
  • 362
  • 661