cJ: conditional Java

Shan Shan Huang, David Zook, Yannis Smaragdakis
Introduction     Example     Download/Installation     cJ Collections Framework     Publication

Introduction

cJ is an extension of Java that allows fields and methods of a class or interface to be provided only under some static subtyping condition. For instance, a cJ generic class, C<P>, may provide a member method m only when the type provided for parameter P is a subtype of a specific type Q.

From a practical standpoint, cJ adds to generic Java classes the ability to express concisely case-specific code that would otherwise result in a combinatorial explosion of program length. Unlike conditional compilation techniques (e.g., the C/C++ #ifdef construct) cJ is statically type safe and maintains the modular type-checking properties of Java generic classes: a cJ generic class can be checked independently of the code that uses it. Just like regular Java, checking a cJ class implies that all uses are safe, under the contract for type parameters specified in the generic class's signature.

Simple Example

A commonly asked question among users of the Java Collections Framework (JCF) is the following: Why don't you support immutability directly in the core collection interfaces so that you can do away with optional operations (and UnsupportedOperationException)? The JCF FAQ gives an excellent, detailed answer. The short of is that there is no good way to support variabilities among different "collections" such as immutability, without an exponential blowup in the number of interfaces required. Using cJ, however, this can easily be done. Here, we show how to express the main interfaces of JCF, Collection, in cJ. For the cJ implementation of the collections framework, see here

First, we need to create three "marker" interfaces that we can use to designate when certain methods exist:

interface Modifiable {}
interface Shrinkable extends Modifiable {}
interface Resizable extends Shrinkable {}
Now we can define the Collection interface. The regular java.util.Collection is parameterized with one type variable, E, which is the type of values contained in the collection. The cJ implementation of Collection adds one more type parameter, M, that is used purely to designate the existence of methods:
public interface Collection<E, M> extends Iterable<E, M> {
    <M extends Resizable> ?
    <
    public boolean add(E o);
    public boolean addAll(Collection<? extends E, ?> c);
    >

    <M extends Shrinkable>?
    <
    public void clear();
    public boolean remove(Object o);
    public boolean removeAll(Collection<?, ?> c);
    public boolean retainAll(Collection<?, ?> c);
    >

    public boolean contains(Object o);
    public boolean containsAll(Collection<?, ?> c);
    public boolean equals(Object o);
    public int hashCode();
    public boolean isEmpty();
    public Iterator<E, M> iterator();
    public int size();
    public Object[] toArray();
    public <T> T[] toArray(T[] a);
}
This new Collection interface says that, when the second type variable it is parameterized with is anything subtyping Resizable, the interface provides all the methods, including add, remove, as well as all the methods that have no side effect on the Collection object. However, a Collection parameterized with a type that is a subtype of Shrinkable, but not a subtype of Resizable, will have methods such as clear, remove, but not add. A Collection type parameterized with the second parameter being Object, for example, would have none of the side-effecting methods, and only methods such as contains, size, etc.

More specifically,

Collection resizableColl = null;
Collection shrinkableColl = null;
Collection unmodifiableColl = null;

resizableColl.add("foo"); // OK
resizableColl.remove("foo"); // OK

shrinkableColl.remove("foo"); // OK
shrinkableColl.add("bar"); // COMPILE_TIME error!!!!

unmodifiableColl.clear(); // COMPILE-TIME error!!!
unmodifiableColl.add("foo"); // COMPILE-TIME error!!!
unmodifiableColl.toArray(); // OK.

Download/Installation Instructions

Requirement: cJ is only verified with jdk version 1.5.0_04.

NOTE: The cJ compiler is an academic prototype product. It is a source-to-source translator: cJ code is translated into Java 1.5 code via erasure. It does not at this time replicate all the type checks that a regular Java compiler does -- for example, it does not check for correct access rights (as signified by modifiers). However, it does all the proper checks needed to ensure that methods and fields exists or not based on the static type conditions that predicate them. We leave the rest of the type checking to the Java compiler installed on your system -- you can run it on the source code generated by the cJ compiler.

Binary

Version Date File(s)
cJ 1.1
April 26, 2006
cJ-1.1.jar

Nightly Builds

Version Date File(s)
2006.04.22
April 22, 2006
cJ-1.0-2006.04.22.jar

Binary Installation Instruction

Download the jar and put it in your classpath.
To invoke the cJ compiler, type:
% java cJ.Main <your-cj-file-or-directory>
A file/directory named __CJ_GENERATED_<your-cj-file-or-directory> will be created, with generated, Java 1.5 compliant source code. You can then run your preferred Java compiler on the generated source code.

Source Code

In order to build cJ from the source code, you will need the following:

  Third Party Software
Java J2SE 1.5.0 Download
ANT 1.6.2 (if you wish to use our build script) Download
ANTLR 2.7.5 Download
  cJ Source Code
cJ 1.0 Source 2006.04.22 cj-1.1.src.tar.gz
cJ 1.0 Source 2006.04.22 cj-1.0-2006.04.22.src.tar.gz
cJ 1.0 Source cj-1.0-src.tar.gz

Source Compilation Instructions

1. Install Java J2SE, ANT, and Antlr according to the instructions on their respective websites. Make sure that their libraries are in your CLASSPATH.
2. Unpack cj-version-src.tar.gz. You will see the following file structure:
    
    antlr.sh            (shell script that invokes antlr on all the grammar 
                         files, and calls ant compile job)
    build.xml           (ANT build script)
    g/                  (cJ ANTLR grammar file directory)
        cJ.parser.g                (Parser for cJ files)
        cJ.typeinfo.tree.g         (Tree walker that collects type information)
	cJ.erasabilityinfo.tree.g  (Tree walker that collects erasability 
                                    information on type parameters)
        cJ.fixuperasedtypes.tree.g (Tree walker that fixes up omitted type 
                                    parameters)
        cJ.memberinfo.tree.g       (Tree walker that collects type member 
                                    information, i.e. fields, methods, etc.)
        cJ.typecheck.tree.g        (Tree walker that type checks cJ files as 
                                    well as translating files being compiled 
                                    into Java 1.5 source code)

    src/
        mj/           
            semant/     (Directory for majority fo cJ type checking code)
            exceptions/ (Directory for cJ exceptions)

        cJ/
     	    Main.java   (java file used to invoke cJ compiler)
            cJAST.java  (AST class, extending ANTLR's AST class)
            cJASTFactory.java (AST factory class)
3. To build all source code, type:
% sh antlr.sh
You will see two directories created for you, bin and lib. All compiled .class files are under the bin directory.

cJ Collections Framework

Binary: ccf.jar    Source: ccf-src.tar.gz

ccf.tar.gz contains the source code for the reimplementation of the Java Collections Framework, in cJ. It is not yet a complete reimplementation of JCF, but it includes the important interfaces such as Iterable, Collection, Map, and some often used classes such as HashMap, Vector.

Note that most of the interfaces/classes have an extra type parameter passed in, used to indicate the existence of methods that have side effects. Collection types parameterized with Resizable as the second type parameter is the most flexible kind, they support both add and remove operations. Shrinkable collections are the ones that can only have items removed. And finally, types parameterized with Object as the second type parameter are the least flexible -- they do not support either add or remove operations. If you happen to call unsupported operations in your code, you will get a compile-time error -- instead of the runtime UnsupportedOperationException you would get in the normal Java Collections Framework!

CAVEAT: Since we had to redefine interface java.lang.Iterable to cj.lang.Iterable, these classes cannot be used with the new for syntax in Java 1.5. We could get around this by rewriting each use of the for syntax into the old syntax. But we have not gotten around to this.

Usage Instructions:

% tar -xzvf ccf-src.tar.gz
This will produce a directory cj. cj/util contains all the JCF classes, such as cj.util.Collection. cj/lang contains redefined interface cj.lang.Iterable.

Now you're ready to write code that use these interfaces/classes. Make sure to compile them with javac version 1.5.0_04 or higher. You must compile the files you wrote together with all the cJ Collections Framework files. This is due to the fact that we have not changed Java reflection to provide information such type conditions on methods, etc. Once you compiled cJ files down to Java, type-conditional information is lost. Of course, this is part of our future work.

Publication


Contact Us