3

I'm not trying to create an array of a generic type (such as E[]). What I'm trying to do is create a predicate that analyzes a (non-primitive) array, such as

public class ArrayPredicate<A extends Object[]> implements Predicate<A> {    

   public boolean test(A a) {
      if(a == null) return false;
      if(a.length == 0) return false;
      for(Object o: a) {
         if(o == null) return false;
      } 
      return true;
   }    

   ...
}

But Predicate<Object[]> is obviously a compiler error.

UPDATE: I need to extend this to StringArrayPredicate and IntegerArrayPredicate, and so on, in order to validate the specific values of each element. Hence, the class itself must be generified.

How can you create a generified predicate for an array?

Community
  • 1
  • 1
aliteralmind
  • 18,274
  • 16
  • 66
  • 102

2 Answers2

2

If you want a Predicate<Object[]>, there's no need to make the implementing class itself generic.

This compiles:

public class ArrayPredicate implements Predicate<Object[]> {    

    public boolean test(Object[] a) {
       if(a == null) return false;
       if(a.length == 0) return false;
       for(Object o: a) {
          if(o == null) return false;
       } 
       return true;
    }    
 }

If you do want to parameterize your class:

public class ArrayPredicate<T> implements Predicate<T[]> {
    public boolean test(T[] a) {
        return a != null && a.length > 0 && !Arrays.stream(a).filter(o -> o == null).findAny().isPresent();
    }
}

Note the java-8 refactoring of your method body if you prefer.

Bohemian
  • 365,064
  • 84
  • 522
  • 658
  • Surprised this compiles. Should've tried it. My need for generic-izing the class itself is to be able to extend this to String[] predicates, and Integer[] predicates... So you can validate the length/value of each element, for instance. – aliteralmind Sep 20 '15 at 04:09
  • 1
    @aliteralmind See edit for generic solution (including java 8 Kung Fu alternative). – Bohemian Sep 20 '15 at 05:06
1

Currently it's not very clear what exactly are you trying to achieve. I assume that you want to create some generic solution to validate all the array elements in the same manner and then specialize it for various element types.

To solve this I'd recommend the following. Make ArrayPredicate<T> not the class, but another interface which extends Predicate<T[]> and provide single abstract method which validates the array element. The whole interface may look like this:

import java.util.Arrays;
import java.util.function.Predicate;

@FunctionalInterface
public interface ArrayPredicate<T> extends Predicate<T[]> {
    public boolean testElement(T t);

    default public boolean test(T[] a) {
        if (a == null || a.length == 0)
            return false;
        return Arrays.stream(a).allMatch(this::testElement);
    }
}

After that you can define specific predicates like this:

public class NonNullArrayPredicate implements ArrayPredicate<Object> {
    @Override
    public boolean testElement(Object obj) {
        return obj != null;
    }
}

public class NonEmptyStringArrayPredicate implements ArrayPredicate<String> {
    @Override
    public boolean testElement(String str) {
        return str != null && !str.isEmpty();
    }
}

And so on. Though the Java-8 way would probably be to remove these classes at all and use lambdas:

ArrayPredicate<Object> nonNullObjectArrayPredicate = Objects::nonNull;
ArrayPredicate<String> nonEmptyStringArrayPredicate = str -> str != null && str.isEmpty();

An alternative and more functional solution would be to get rid of ArrayPredicate at all and create single higher-order function instead which translates an element predicate to array predicate:

// In any suitable class
public static <T> Predicate<T[]> forArray(Predicate<T> elementPredicate) {
    return a -> a != null && a.length != 0 &&
                Arrays.stream(a).allMatch(elementPredicate);
    };
}

This way you can create specific predicates like this:

Predicate<Object[]> nonNullObjectArrayPredicate = forArray(Objects::nonNull);
Predicate<String[]> nonEmptyStringArrayPredicate = forArray(
                                     str -> str != null && str.isEmpty());

So you don't need to declare any new classes, just functions.

Tagir Valeev
  • 87,515
  • 18
  • 194
  • 305