Skip to content

Instantly share code, notes, and snippets.

@briangoetz
Created November 25, 2013 22:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save briangoetz/7650431 to your computer and use it in GitHub Desktop.
Save briangoetz/7650431 to your computer and use it in GitHub Desktop.
Flow typing
Proponents of "flow typing" have suggested, in code like this:
if (foo instanceof Bar) {
((Bar) foo).barMethod();
}
"why not just" allow the developer to elide the cast to Bar? Implicit in this question is the assumption that this is a very simple feature.
Like everything else, the easy cases are easy. The key question to ask is, under such a scheme, if the static type of foo outside the if-block is Moo, what is the static type of foo inside the if block?
One obvious, but wrong, candidate is Bar; this means that foo could no longer be used as a Moo:
if (foo instanceof Bar) {
...
foo.mooMethod(); // compile error: mooMethod() not a member of Bar
}
The less-wrong answer is an intersection type: (Moo & Bar). Now, imagine this overloading of existing methods:
void m(Moo m) { ... }
void m(Bar b) { ... }
and we have existing code like this:
if (foo instanceof Bar) {
m(foo);
...
}
But if we reinterpret the static type of foo as Moo & Bar, this code no longer compiles, due to an ambiguity in overload resolution. So this language change, as envisioned, is not source-compatible. Oops.
Now, one could layer arbitrarily complicated new rules for overload resolution to try and "fix" this problem, but now we're well outside the territory of an "easy" feature.
@briangoetz
Copy link
Author

You may be getting caught up on the distinction between the static and dynamic types of a variable? Consider:

Object x = new ArrayList<String>();

The static type (compile-time type) of x is Object. The dynamic (runtime) type of x is ArrayList. Many features depend on both static and dynamic types; for example, method overload selection is done off of the static type of the arguments; the overload is selected statically, and then dynamically dispatched on the runtime type of the receiver. But some only depend on static types, and some (like instanceof) depend only on dynamic types.

You don't need to have a static subtyping relationship for the cast to work; only a dynamic one. However, there are some casts that the compiler can statically prove cannot succeed (and IDEs and FindBugs will flag these for you.) For example:

ArrayList<String> x = new ArrayList<>();
if (x instanceof Serializable) { ... }  // might succeed

if (x instanceof LinkedList) { ... } // statically provable that this will not succeed

@mikaelgrev
Copy link

Ah, OK. Thanks for clearing that up.

I do of course know the difference but I didn't think that would matter here. Luckily you did. :)

Cheers,
Mikael

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment