Thursday, 16 July 2009

Fragile predicates with Java's booleans

Interoperability with the JVM is a wonderful thing but it can be the root of some surprises. The following seemingly innocuous Clojure snippet produces a surprising result:

user=> (if (Boolean. false) "true" "false")
"true"

Specifically, we used a predicate that appears to be false but the true branch of the if expression was taken. This occurs because Clojure uses a boxed representation of booleans that is incompatible with Java's. Combined with Clojures dynamic typing, this causes Java's false to be interpreted as not Clojures false and, therefore, true.

The solution is to box Java booleans in order to obtain a Clojure boolean:

user=> (if (boolean (Boolean. false)) "true" "false")
"false"

1 comment:

  1. The real solution is to produce Java Booleans using Boolean.valueOf and avoid the Boolean constructor, as indicated by its documentation:

    http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Boolean.html#Boolean(boolean)

    user=> (if (Boolean/valueOf false) "true" "false")
    "false"
    user=> (if (Boolean/valueOf "false") "true" "false")
    "false"

    Note, Clojure's false and true are already Java Booleans, so this is not about boxing compatibility:

    user=> (class false)
    java.lang.Boolean

    The boolean coercion function should be reserved for improperly constructed Booleans you get from someone else. Unfortunately, one of those "someone elses" is Java's own reflection API - aargh!

    ReplyDelete