3

I'm using Spring Boot under Java 8 and I'm trying to perform a type cast inside a lambda expression, but can't get it to work after a lot of research and try/error.

Suppose I have a list like this:

List<Foo> list;

that is filled in a step of a batch process, and because of datasource restrictions, the class Foo needs to be declared like this (with all its members as String):

class Foo {

    private String id;
    private String quantity;

    public String getId() {
      return id;
    }

    public void setId(String id) {
      this.id = id;
    }

    public String getQuantity {
      return quantity;
    }

    public void setQuantity(String quantity) {
      this.quantity = quantity;
    }
}

Once the list is loaded, I need to do some grouping and summing based on id, given that the quantity String represents a decimal number.

The following code gives me an error because summingDouble is expecting a Double, not a String:

Map<String, Double> grouping = list.stream().collect(Collectors.groupingBy(Foo::getId, Collectors.summingDouble(Foo::getQuantity)));

I managed it to fix the error by adding an extra function to Foo class, like this:

public Double getQuantityAsDouble() {
    return StringUtils.isEmpty(quantity) ? new Double(0) : Double.valueOf(quantity);
}

and doing the call this way:

Map<String, Double> grouping = list.stream().collect(Collectors.groupingBy(Foo::getId, Collectors.summingDouble(Foo::getQuantityAsDouble)));

but it seems a poor solution to me.

Is there any way to direct cast Foo::getQuantity from String to Double inside Collectors.summingDouble() without the need to add bloat functions to the original bean?

Thanks!

2
  • A String is not a Double, so casting is not possible. Using Double.valueOf` to do the conversion is a good solution. Commented Feb 14, 2018 at 16:16
  • Your private method is in fact a very good solution. It encapsulates the default value (in case the string doesn't represent a number) and the conversion. Commented Feb 14, 2018 at 17:06

1 Answer 1

3

Java doesn't do implicit conversion from String to Double, which is what your code is implying.

You need an explicit conversion, which can be accomplished with with a conversion in the stream:

Map<String, Double> grouping = list.stream().collect(Collectors.groupingBy(Foo::getId,
            Collectors.summingDouble(foo -> Double.parseDouble(foo.getQuantity()))));

(or use Double.valueOf if those semantics are more appropriate)

EDITED: clarity

EDITED MORE: to add explanation based on comment.

The double colon syntax (e.g. Foo::getQuantity) creates a method reference. Double.valueOf only knows how to deal with a string. In this case, java needs some "imperative help" to connect the dots, hence the imperative lambda, rather than the function reference.

Sign up to request clarification or add additional context in comments.

1 Comment

I tried some syntax like: Collectors.summingDouble(Double.valueOf(Foo::getQuantity)) but it didn't worked. The correct one was that one you point: Collectors.summingDouble(foo -> Double.valueOf(foo.getQuantity())). Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.