3

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;
  }
Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
Shvalb
  • 1,835
  • 2
  • 30
  • 60
  • 1
    *It doesn't build* ... but what does the error read? Tried debugging? For the Edit-1, did you `trim` the Strings? – Naman Jun 04 '21 at 14:53
  • Please provide the stacktrace and error message, so this question is helpful to other users – fps Jun 04 '21 at 14:56

1 Answers1

5

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;
}
Holger
  • 285,553
  • 42
  • 434
  • 765