Note: information on this page refers to Ceylon 1.0, not to the current release.

Using Ceylon from Java

Description

This page covers how you can use Ceylon classes, interfaces etc from Java.

Important Note: Everything documented here is subject to change until Ceylon 1.0 and has changed in M3.

Declaring Ceylon types

In general, a top level Ceylon types and inner classes compile into a Java type of the same name.

The situation is more complicated for inner interfaces. The compiler always generates a top level interface, using the containing type name(s) separated with a dollar ($) and then the interface name, again separated with a dollar. For example this Ceylon:

class C() {
    interface I {
    }
}

results in an interface like this:

interface C$I {
}

This is done in order to support arbitrary nesting of interfaces and classes within other types, which cannot be expressed in Java.

Instantiating Ceylon classes

Ceylon types are instantiated like every Java type. They have an overloaded constructor for each defaulted parameter.

For example suppose you have the following Ceylon class:

shared class Foo(Integer n = 5, Integer m = n + 1) {
}

Then in Java you can instantiate a Foo in three ways:

Foo foo1 = new Foo(6, 7);
Foo foo2 = new Foo(6); // use the default m
Foo foo3 = new Foo();  // use the default n and m

Accessing Ceylon attributes

If the Ceylon attribute is annotated shared the Java accessor will be declared public, otherwise it will be annotated private.

Instance attributes

In general a Ceylon instance attribute compiles into a Java Bean-style getter and (if the attribute is variable or has an assign block) setter.

Boolean attributes use get accessors rather than an is accessors. Note that a get accessor is still valid for boolean properties according to the Java Bean specification.

Toplevel attributes

A toplevel attribute is compiled into a class of the same name with an underscore (_) suffix, which is final, has a private constructor, a static getter method and if the attribute is mutable, a corresponding static setter method.

Toplevel attributes like the following:

shared variable Boolean bool = true;

Are accessed and set using the following Java code:

// bool is the name of the Java class that holds the toplevel attribute 
boolean value = bool_.getBool$();
bool_.setBool$(false);

Calling Ceylon methods

In general a Ceylon method compiles directly into a Java method of the same name. The method result type and argument types may not however translate directly because of the type mapping rules.

If a Ceylon method name is a Java keyword, it will be prefixed with $, like $true.

Calling Ceylon toplevel methods

A Ceylon toplevel method is compiled into a class of the same name with an underscore (_) suffix, which is final, has a private constructor, and a static method of the same name corresponding to the toplevel method.

Toplevel methods like the following:

shared Boolean foo(Boolean b){
    return b;
}

Are called using the following Java code:

// foo is the name of the Java class that holds the toplevel method 
boolean value = foo_.foo(false);

Calling a Ceylon method with default parameter values

Ceylon methods can have default parameter values. There will be an overloaded method for each defaulted parameter.

For example suppose you have the following Ceylon class:

shared class Foo() {
    shared void foo(Integer n = 5, Integer m = n + 1) {}
}

Then in Java you can invoke Foo.foo() in three ways:

Foo f = new Foo();
f.foo(6, 88);
f.foo(6); // use the default m
f.foo();  // use the default n and m

Methods and initialisers with varargs

Method and initialisers can have varargs, which are represented in the resulting Java code as a Iterable<? extends T> parameter. There will be an overloaded method if you want to use the sequenced parameter's default value (an empty Iterable by default)

If you wish to invoke a Ceylon method or initialiser that supports varargs you need to use the following idiom:

// Foo.foo supports varargs of String...
Foo f = new Foo();
// use the parameter's default sequence
f.foo();
// pass no arguments
f.foo(ceylon.language.$empty.getEmpty());
// pass two arguments
f.foo(new ceylon.language.ArraySequence(ceylon.language.String.instance("a"), 
                                        ceylon.language.String.instance("b")));

Catching Ceylon exceptions

The root of the exception hierarchy in Ceylon is ceylon.language.Exception, (a subclass of java.lang.RuntimeException at runtime). This means that pure Ceylon code can only generate unchecked exceptions.

Impure Ceylon (that is, Ceylon code which access Java code) may throw any exception that is thrown by that Java code, including checked exceptions. Ceylon methods do not declare throws java.lang.Throwable in their bytecode, even though they can in principle throw Throwable. In practice Ceylon methods are very unlikely to throw Throwable itself (because Java methods are unlikely to), but Ceylon methods are quite likely to throw java.lang.Exception. So unless you know otherwise, it's probably sensible to wrap calls to Ceylon methods in a Java-side try/catch which handles this possibility.

Type conversions

If you need to convert a Java or Ceylon type according to the type mapping rules, you can do so using the following methods.

Convert from a Java type to a Ceylon type

Java type Convert to Ceylon object
boolean b ceylon.language.Boolean.instance(b)
byte x, short x, int x, long x ceylon.language.Integer.instance(x)
float x, double x ceylon.language.Float.instance(x)
char c ceylon.language.Character.instance(c)
T[] a ceylon.language.Array.instance(a)
java.lang.String s ceylon.language.String.instance(s)

Convert from a Ceylon type to a Java type

Ceylon type Convert to Java object
ceylon.language.Boolean b b.booleanValue()
ceylon.language.Integer i i.longValue()
ceylon.language.Float f f.doubleValue()
ceylon.language.Character c c.intValue()
ceylon.language.Array a a.toArray()
ceylon.language.String s s.toString()

Annotating Java declarations with Ceylon annotations

Ceylon annotation classes are compiled into Java annotation types (@interfaces). Each annotation class parameter is mapped to an annotation type member. This means it's mostly a matter of adding the Ceylon annotation types to your Java declarations.

  • The name of the annotation type is the name of the annotation class with $annotation appended
  • Annotation classes that subclass SequencedAnnotation have an extra wrapper annotation type, which holds an array of the individual annotations. The name of this wrapper annotation type is the name of the annotation class with $annotations appended.
  • Annotation classes that subclass ConstrainedAnnotation have their program element constraints transformed into a @Target constaint. However, due to the differing semantics for constraining annotations it's not a bijective mapping.
  • Due to the constraints placed on annotation types by the Java language and virtual machine to type mapping differs
    • Iterable, Sequence and Tuple-typed parameters are all mapped to a Java array of the relevant type.
    • Declaration references are mapped to java.lang.String using a special syntax detailed below.
    • objects of enumerated types mapped to the java.lang.Class for the anonymous class.

The grammar for the Declaration reference syntax is as follows:

ref              ::= version? module ;
                     // note: version is optional to support looking up the
                     // runtime version of a package, once we support this
version          ::= ':' SENTINEL ANYCHAR* SENTINEL ;
module           ::= dottedIdent package? ;
dottedIdent      ::= ident ('.' ident)* ;
package          ::= ':' ( relativePackage | absolutePackage ) ? ( ':' declaration ) ? ;
                     // note: if no absolute or relative package given, it's the 
                     // root package of the module
relativePackage  ::= dottedIdent ;
absolutePackage  ::= '.' dottedIdent ;
                     // note: to suport package names which don't start 
                     // with the module name
declaration      ::= type | function | value ;
type             ::= class | interface ;
class            ::= 'C' ident ( '.' member )?
interface        ::= 'I' ident ( '.' member )?
member           ::= declaration ;
function         ::= 'F' ident ;
value            ::= 'V' ident ;

For example the ClassDeclaration for ceylon.language::String in ceylon.language version 0.6 would be ::0.6:ceylon.language::CString, and for the value true it would be ::0.6:ceylon.language::Vtrue.

See also