Type safety in functional interfaces of Java

Viewed 80

I have reached the limit of my ability to deal with Java's type system. The scenario is this:

I have the need for a method that can take a Collection<Comparable> and a function that takes a Collection<Comparable> as an input and returns a single element from that collection.

That seems to be done like this:

private <T extends Comparable<T>> Comparable<T> applyFunctionOnStuff(
        final Collection<Comparable<T>> stuff,
        final Function<Collection<Comparable<T>>, Comparable<T>> function) {
    return function.apply(stuff);
}

But then I have the inability to bend Collections::max to be a valid input for the function. I was wondering whether there is a way for me to wrap static methods like that into a Function class and use them inside this method.

3 Answers

You have a bit of redundancy in your code as your Type T is defined as T extends Comparable<T> and then everytime you refer to your type T you use it as follows: Comparable<T> which should actually be just T itself.

So your code should look like this:

private <T extends Comparable<T>> T applyFunctionOnStuff(
    final Collection<T> stuff,
    final Function<Collection<T>, T> function) {
    return function.apply(stuff);
}

Then you can use Collections::max without any further errors

I think it's fine if you accept your own answer, but I'd like to point out that Comparable must usually be declared as Comparable<? super T> or you'll end up with generic type weirdness. Your return type should be T, not Comparable<T>.

I also still don't see how what you're doing is easier than just calling the function in question directly. Your method appears to obfuscate the code more than necessary.

public class FunctionalComparable {
   public static void main( String[] args ) {
      Collection<String> names = new ArrayList<>( Arrays.asList( 
              "Larry", "Moe", "Curly" ) );

      // why not just this?
      System.out.println( Collections.max( names ) );

      // this seems more work than necessary
      System.out.println( doIt( names, Collections::max ) );
   }

   private static <T extends Comparable<? super T>> T doIt(
           final Collection<T> collection, 
           final Function<Collection<T>, T> function ) 
   {
      return function.apply( collection );
   }
}

Fixed it:

private <T extends Comparable<T>> Comparable<T> applyFunctionOnStuff(
        final Collection<T> stuff,
        final Function<Collection<T>, T> function) {
    return function.apply(stuff);
}

That seems to magically take an input like applyFunctionOnStuff(new ArrayList<Integer>(), Collections::max);

Related