6

I'm having trouble following the below code snippet:

prices = pricesService.getProductsByCategory(category);
List<Double> discountedPrices = 
    Lists.newArrayList(Iterables.transform(prices, new Function<Double, Double>() {
        public Double apply(final Double from) {
            return from *.88;
        }
    }));

I know what the result of the code is and it's correct in unit tests, but I'm not overly familiar with guava or how/why this implementation works. Also currently it doesn't appear to be safe if there is a null value in the list 'prices' either? So what I'm after:

  1. A general explanation of how the code works.
  2. Is it currently null safe? If not how can it made to be?
Ondra Žižka
  • 43,948
  • 41
  • 217
  • 277
Joe Schmuck
  • 309
  • 3
  • 7
  • 1
    Which part is giving you trouble? Have you read the documentation for `.transform`? It would seem like if nothing in the chain filters out nulls, how to make it null-safe would be fairly obvious, although you'd need to define what you'd actually want to have happen in that case. – Dave Newton Jul 10 '13 at 03:50
  • I guess it just looks a little foreign to me. Seeing 'transform' as the method name is kinda messing with my head. As far as the null part, I would just check if from == null I guess? Wasn't sure if there was something different going on with the Function call? – Joe Schmuck Jul 10 '13 at 03:51
  • What's wrong with `transform` as a method name? – Dave Newton Jul 10 '13 at 03:54
  • For me transform feels like it implies applying changes an existing structure, and this appears to return a new one? I guess nothing wrong with it, just wasn't lining up right in my head. – Joe Schmuck Jul 10 '13 at 03:57
  • 3
    When you understand what this code is doing, make sure to read: https://code.google.com/p/guava-libraries/wiki/FunctionalExplained (and test both the imperative and functional approaches for performance). – cfeduke Jul 10 '13 at 03:59
  • 3
    "Null safe" is such a misleading term. Another phrase might be "won't tell you about null time bombs in your code that probably indicate a subtle error." – Louis Wasserman Jul 10 '13 at 04:15

2 Answers2

6

It creates a new List of Doubles which are 0.88 * the original.

The constructs are:

Anonymous inner class

This is a way how callbacks / closures are sometimes done in Java. See also Java tutorial on this.

new Function<Double, Double>() {
    public Double apply(final Double from) {
        return from *.88;
    }
}

Callback using the above function

Iterables.transform(prices, *func*)

Converting the result to ArrayList

The result of the above is an Iterable, so it needs to be stored to a list. See also Lists.newArrayList vs new ArrayList

Lists.newArrayList( ... )
Community
  • 1
  • 1
Ondra Žižka
  • 43,948
  • 41
  • 217
  • 277
  • So more or less this was a functional programming style of solving this problem? Haven't seen much functional behavior in Java! Great links too, thanks! – Joe Schmuck Jul 10 '13 at 04:08
  • Kind of, yes... there is probably going to be a syntactic shorthand in next Java SE, and this style is going to get a bit briefer, in the Groovy style. – Ondra Žižka Jul 10 '13 at 04:13
  • Btw this style is used a lot in some web frameworks, eg. Wicket – Ondra Žižka Jul 10 '13 at 04:14
2

1) So Guava has a static util class callled Iterables that has a method called transform that takes a collection and a guava Function instance as variables. In this case the developer used an in line anonymous function that returns a double value by the overridden method "apply".

A more traditional implementation would have been something like this:

List<Double> discountedPrices = Lists.newArrayList();
for(Double price: prices) {
    discountedPrices.add(price * .88);
}

2) Not entirely sure what you mean by null safe? assuming you mean what would happen if the list 'prices' contained a null value? If so guava has another solution for you in Iterables.filter(Collection, Predicate). In your case you'd want to filter out nulls and there is a built in guava Predicate for this purpose. So in your case you could do something like:

 prices = Iterables.filter(prices, Predicates.notNull();
 List<Double> discountedPrices = Lists.newArrayList(
 Iterables.transform(prices, new Function<Double, Double>() {
        public Double apply(final Double from) {
            return from *.88;
        }
    }));

This first line returns the prices collection without nulls in it and the 2nd would behave exactly as before and you could safety assume nulls had been removed already.

Durandal
  • 5,575
  • 5
  • 35
  • 49
  • 4
    The `filter` has to be applied before the `transform`, not after. Otherwise the implicit unboxing in `from *.88` will throw a `NullPointerException`. – Matt McHenry Jul 10 '13 at 03:56