Class

A class is a stateful type that:

  • may hold references to other objects,
  • may define initialization logic and initialization parameters, and
  • except in the case of an abstract class, may be instantiated.

A class may inherit another class, but classes are restricted to a single inheritance model. That is, a class inherits exactly one other class. Since single inheritance is quite often too restrictive, a class may also satisfy an arbitrary number of interfaces.

Usage

A trivial class declaration looks like this:

class C() {
    /* declarations of class members */
}

Description

Initializer

The class initializer executes when instances of the class are created (also known as class instantiation). The parameters to the initializer are specified in parenthesis after the name of the class in the class declaration.

The body of a class must definitely initialize every member of the class. The following code will be rejected by the compiler:

class C(Boolean bool) {
    shared String greeting;
    if (bool) {
        greeting = "hello";
    }
}

Extending classes

The extends clause specifies the superclass of a class, and arguments of the initializer parameters of the superclass. The arguments to the superclass initializer are specified in parentheses after the name of the superclass:

class S() extends C() {
    /* declarations of class members */
}

If a class is declared without using the extends keywords, it is a subclass of Basic.

Satisfying interfaces

The satisfies keyword specifies the interfaces inherited by a class:

class C() satisfies I1 & I2 {
    /* declarations of class members */
}

If a class is declared without using the satisfies keyword, it does not directly inherit any interfaces. However, it may indirectly inherit interfaces via its superclass.

& is used as the separator between satisfied interfaces because C is being defined as a subtype of the intersection type I1&I2.

Enumerated classes

The subclasses of an abstract class can be constrained to a list of named classes or toplevel anonymous classes using the of clause. If the class C is permitted only two direct subclasses, S1 and S2, its declaration would look like this:

abstract class C() of S1 | S2 {
    /* declarations of class members */
}

Generic classes

A generic class declaration lists type parameters in angle brackets (< and >) after the class name.

class C<Z>() {
    /* declarations of class members 
       type parameter Z treated as a type */
}

A class declaration with type parameters may have a given clause for each declared type parameter to constrain the argument types.

Initializer parameters

Every class declaration has a parameter list.

Callable type

A class may be viewed as a function that produces new instances of the class. The callable type of a class expresses, in terms of the interface Callable, the type of this function.

For example the callable type of

class Example(Integer int, Boolean bool) => "";

is Example(Integer, Boolean).

(Regular functions also have a callable type.)

Concrete classes

A class that can be instantiated is concrete. It follows that abstract or formal classes are not concrete.

Abstract classes

An abstract class is a class that may not be instantiated. Abstract classes may declare formal members. An abstract class declaration must be annotated abstract:

abstract class C() {
    /* declarations of class members */
}

Naturally, abstract classes compete with interfaces, since both an abstract class and an interface may contain a mix of concrete and formal members. The crucial difference is:

  • an abstract class may contain or inherit state and initialization logic, whereas
  • interfaces support a full multiple inheritance model.

Nevertheless, it is often unclear whether a certain situation calls for an interface or an abstract class. Our advice is to incline in favor of using an interface, where reasonable.

shared classes

A toplevel class declaration, or a class declaration nested inside the body of a containing class or interface, may be annotated shared:

shared class C() {
    /* declarations of class members */
}
  • A toplevel shared class is visible wherever the package that contains it is visible.
  • A shared class nested inside a class or interface is visible wherever the containing class or interface is visible.

formal classes

A class declaration nested inside the body of a containing class or interface may be annotated formal. A formal class must also be annotated shared.

Like abstract classes, formal classes may have formal members. Unlike abstract classes, formal classes may be instantiated.

A formal class must be refined by concrete subclasses of the containing class or interface.

default classes

A class declaration nested inside the body of a containing class or interface may be annotated default. A default class must also be annotated shared.

A default class may be refined by types which inherit the containing class or interface.

Members

The permitted members of classes are classes, interfaces, methods, attributes, and objects.

Aliases

A class alias is a kind of alias.

Member class refinement

An inner class of a class or interface can be subject to member class refinement, which means its instantiation will be polymorphic.

Here's an example where a Reader class declares that concrete subclasses must (because we used formal) provide an actual Buffer inner class.

shared abstract class Reader() {
    shared formal class Buffer(Character* chars)
            satisfies Sequence<Character> {}
    // ...
}

shared class FileReader(File file) 
        extends Reader() {
    shared actual class Buffer(Character* chars)
            extends Reader::Buffer(chars) {
        // ...
    }
    // ...
}

Within Reader (and elsewhere) we can instantiate the relevant kind of Buffer with a normal instantiation, Buffer(chars). This allows each subclass of Reader to implement an appropriate kind of Buffer.

Member class refinement is a lot like the 'abstract factory' pattern in other object-oriented languages, but it's a lot less verbose.

Only formal and default member classes are subject to member class refinement. A formal member class must be refined by concrete subtypes of the type declaring the member class—just like a formal method or attribute. A default member class may be refined—just like a default method or attribute.

In a subtype of the type declaring the member class, the member class (i.e. in FileReader.Buffer from the example above) must:

  • be declared actual,
  • have the same name as the member class in the declaring type (Buffer in the example),
  • have a parameter list with a compatible signature and,
  • extend the member class (you'll need to use super in the extends clause).

Refined member types are similar to, but not the same as, virtual types, which Ceylon does not support.

Metamodel

Class declarations can be manipulated at runtime via their representation as ClassDeclaration instances. An applied class (i.e. with all type parameters specified) corresponds to either a Class or MemberClass model instance.

See also