Thursday, June 10, 2010

An Illustrative Problem (Part II) correction

Blogger really munged Mike's code, so here is the relevant part correctly formatted:
public static int minimumIntegerInBox(Collection<Box<?>> boxes) {
  Iterable<Box<?>> intBoxes = filter(boxes,
      new Function<Box<?>, Boolean>() {
        @Override
        public Boolean of(Box<?> domainItem) {
          return domainItem.get() instanceof Integer;
        }
      });
  Iterable<Integer> ints = map(
      intBoxes,
      new Function<Box<?>, Integer>() {
        @Override
        public Integer of(Box<?> domainItem) {
          return Integer.class.cast(domainItem.get());
        }
      });
  return minimumElement(ints);
}

public static int minimumElement(Iterable<Integer> items) {
  return best(
      new Function<Pair<Integer, Integer>, Boolean>() {
        @Override
        public Boolean of(Pair<Integer, Integer> domainItem) {
          return domainItem.second() < domainItem.first();
        }
      },
      items);
}
This is similar to what I started with, but I didn't like this solution for two reasons.
  1. The test for whether the Box<?> is a Box<Integer> relies on extracting the contents of the Box and testing to see if it is an integer. In other words, rather than relying on the type of the container, we explicitly look at the contents of the container. This is an important distinction, but it might be hard to see it in this context. Just because the box happens to contain an integer at this point doesn't mean that it is a Box<Integer>. It could be a Box<Number> or a Box<Object> that currently has an integer in it, but could just as well have some non-integer in it. Conversely, it could be a Box<Integer> that happens to have a null in it. For the purposes of finding the minimum element, a null would be an issue, but if the task were to “fill all the Box<Integer> with zero,” then this method of testing the box type would miss the Box<Integer> with null.

  2. The type of intBoxes is wrong. It really ought to be Iterable<Box<Integer>>.

Anyone care to offer a fix?

3 comments:

katre said...

Due to type erasure, you can't tell the difference between Box<Object> and Box<Integer> at runtime in Java. Generic types only exist during compilation. During runtime, they're just regular types that gets to skip some of the cast correctness checks.

Jrm said...

Right, which is why there's that Integer.class.cast in the map clause.

Steve said...

For this to work like you want, Java style, you'd have to add a Class member to the Box class, to represent the type of Box at runtime. You could then write a static generic filter taking an Iterable of generic Boxes and a Class object to filter by, and returning an Iterable of the correct type fairly simply. Dunno if that floats your boat, tho.