Daniel Pitts’ Tech Blog

Posts Tagged ‘type intersection’

Almost Useful: if instanceof

Friday, November 23rd, 2007

While often times if (x instanceof Y) indicates a problem that should be solved with proper use of polymorphic methods, it sometimes is unavoidable (.equals(Object o) implementation for example). A common idiom is:

if (x instanceof Y) {
  Y y = (Y) x;
}

Of course, this makes sense from a utility point of view, you know that it is a Y object, and so you want Y functionality from it. From a programmers point of view though, it is terribly redundant in many ways. In many cases, static analysis can tell the compiler that when you are in the block of that if statement, that x is indeed a Y object, why then should I be forced to cast to Y? Why can’t the compiler figure out the type for me?

This becomes even more useful if we combine the concept with the fully useful Type Intersection (Not yet implemented). If both of these features were implemented, then you could write something like:

public <T extends Comparable<T>> void sort(List<? extends T> list) {
 if (list instanceof RandomAccess) {
    /* list compile-time type here would be List<? extends T>&RandomAccess. */
    sort(list);
  } else {
    /* Other type of sort implementation. */
  }
}
public <T extends Comparable<T>> void sort(List<? extends T>&RandomAccess list) {
  /* Random access sort implementation */
}

This type of static analysis feature could be extended to predict NPE’s and redundant checks. Heck, the IDE I use tells me when I’ve accessed something when it might be null, or when my inner-if statement is a subset of the outer. It even has shortcuts for adding the cast in the if (x instanceof Y) {} blocks.

Almost Useful: Java Type Intersection.

Friday, November 23rd, 2007

First, for my subscribers that celebrate it, Happy Thanksgiving!

I’ve written about it before, but I think its worth revisiting. Type intersection would be a highly useful feature if not for one thing. “It is not possible to write an intersection type directly as part of a program; no syntax supports this.” - JLS (§4.9). That seems like a poor excuse to not support a feature as potentially powerful as type intersection could have been.

They were bold enough to add syntax all over the place for Generics. They added new syntatical meanings for ‘?’ ‘<’, and ‘>’. As a matter of fact, they added a syntax within that construct for handling Type Intersections. Would it really be that difficult to reuse that syntax outside of capture conversions and type inference? Heck, maybe even make it reifiable, although that’s not *as* important.

One example others have used in the past where it would be useful to have this type intersection is with the marker interface RandomAccess. While marker interfaces are less useful now that we have annotations, it none-the-less exists, and can be useful for ensuring that the user of an algorithm passes in a compatible list.

For example, its quite possible to do the following, even with the currently crippled implementation of type intersection.
<T extends List<String> & RandomAccess> void foo(T list) { ... }
You know that you’re getting a random access list. The pain point is that you can not do the following:
<T extends List<String> & RandomAccess> T foo() { return new ArrayList<String>(); }
The reason that isn’t legal is quite simple, even if its not obvious. T is any type that satisfies List<String>&RandomAccess, so you don’t know that it is an ArrayList. You might have MyNonArrayList<String> list = foo(); Oops, that would be an incompatible assignment.

The better approach would be to have the return type be an explicit type that is List<String>&RandomAccess. As a matter of fact, my suggestion is to use that syntax exactly, unless there is a compiler-grammar reason not to. So, our T foo() line becomes:
List<String>&RandomAccess foo() { return new ArrayList<String>(); }
So then we can do: List<String>&RandomAccess list = foo(); Actually, we could just use List<String> list=foo() if we don’t care about RandomAccess.

An important addition to make to this would be casting. For legacy support, if I have a List<String>, but I know that it should be an ArrayList (or some other RandomAccess), I should be able to cast: foo((List<String>&RandomAccess)list);

Type Intersection in Java, or: Interest in Interfaces is Invaluable.

Tuesday, March 6th, 2007

There was an interesting philosphical question posted in the comp.lang.java.programmer newsgroup the other day. Patricia, the original poster, had a question about using ArrayList as the declared type. She wanted to convey the fact that she wanted a List which has constant time random access methods, as this is an important property for her algorithm. Many of us live by the rule “Always use an interface, never a concrete type,” and she was questioning this. I personally think this dogma is a little to broad. However, it usually makes sense when using Java collections. As a matter of fact, you’ll find a lot of my code uses Collection instead of Set or List.

I guess Sun also thought it was important to convey constant time random access; they created an interface called RandomAccess just for that. RandomAccess is a marker interface for a List implementation to “indicate that they support fast (generally constant time) random access.”

Thats all well and good, but there’s a problem here.

   RandomAccess myList = new ArrayList<Integer>(); // This compiles fine

   myList.get(0); // Whoops, compile error. RandomAccess doesn't extend List, so no get method;

Java 1.5 *almost* supports what we want… We want to say “Give me an object that is RandomAccess AND List<Integer>”. With the introduction of Generics, Java introduced Type Intersection. Unfortunately, they only introduced it in the context of Generics.

  // Processes a RandomAccess List of integers, compiles fine

  public <L extends RandomAccess&List<Integer> > void processList(L list) {
      // ...
  }

  // Fails to compile.
  public List<Integer> process(Collection<Integer> input) {
    RandomAccess&List<Integer> output = new ArrayList<Integer>(input);
    //  ...
    return output;
  }

While this is too convoluted to answer Patricia’s philosophical question. It does bring us to an interesting, if incomplete, feature of Java.

(more…)