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.
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.
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.
In order to build cJ from the source code, you will need the following:
|
Third Party Software |
|
|
|
cJ Source Code
|
|
|
|
|
|
|
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