How to pass valueOf method which has several overrides as parameter to method



I’m interested to write a generic split method, that can be invokes like this:

List<String> names = splitField("John, Nate, Larry", String::valueOf);
List<Integer> ages = splitField("35, 23, 48", Integer::valueOf);

Here is my implementation:

private <T, R> List<R> splitField(String stringWithComma, Function<T, R> valueOf) {
    List<R> elements = new ArrayList<>();
    if (stringWithComma != null && !stringWithComma.isEmpty()) {
        elements = Arrays.stream(stringWithComma.split(","))
                .map(valueOf)
                .collect(Collectors.toList());
    }
    return elements;
  }

It doesn’t build, What am I missing here?

Thanks!

EDIT 1:

This implementation builds and works well went invoked with String::valueOf, but throws exception when invoked on Integer::valueOf

private <R> List<R> splitField(String elementsWithComma, Function<String, R> valueOf) {
    List<R> elements = new ArrayList<>();
    if (elementsWithComma != null && !elementsWithComma.isEmpty()) {
        elements = Arrays.stream(elementsWithComma.split(","))
                .map(valueOf)
                .collect(Collectors.toList());
    }
    return elements;
  }

Answer

You are declaring a type parameter T where you actually want String, as the function is supposed to process a value you know to be a String to R. Remove the type parameter T and change the function to Function<String, R>.

private <R> List<R> splitField(String stringWithComma, Function<String, R> valueOf) {
  List<R> elements = new ArrayList<>();
  if (stringWithComma != null && !stringWithComma.isEmpty()) {
      elements = Arrays.stream(stringWithComma.split(",\s*"))
              .map(valueOf)
              .collect(Collectors.toList());
  }
  return elements;
}

Note that I also change the split pattern to let it consume the space after commas, to avoid parse exceptions.

Following the PECS rule, you can relax the function signature, to support more use cases.

private <R> List<R> splitField(
        String stringWithComma, Function<? super String, ? extends R> valueOf) {
  List<R> elements = new ArrayList<>();
  if (stringWithComma != null && !stringWithComma.isEmpty()) {
      elements = Arrays.stream(stringWithComma.split(",\s*"))
              .map(valueOf)
              .collect(Collectors.toList());
  }
  return elements;
}


Source: stackoverflow