Kawa, the Java-based Scheme system

Node:Defining new classes, Next:Anonymous classes, Previous:Field operations, Up:Objects Classes and Modules

### Defining new classes

Kawa provides various mechanisms for defining new classes. The `define-class` and `define-simple-class` forms will usually be the preferred mechanisms. They have basically the same syntax, but have a couple of differences. `define-class` allows multiple inheritance as well as true nested (first-class) class objects. However, the implementation is more complex: code using it is slightly slower, and the mapping to Java classes is a little less obvious. (Each Scheme class is implemented as a pair of an interface and an implementation class.) A class defined by `define-simple-class` is slightly more efficient, and it is easier to access it from Java code.

The syntax of `define-class` are mostly compatible with that in the Guile and Stk dialects of Scheme.

define-class name (supers ...) field-or-method-decl ... | Syntax
define-simple-class name (supers ...) field-or-method-decl ... | Syntax

              field-or-method ::= field-decl | method-decl
              field-decl ::= (fname [:: ftype] [option-keyword option-value]*)
              method-decl ::= ((method-name formal-arguments) [:: rtype] body)
Defines a new class named name. If `define-simple-class` is used, creates a normal Java class named name in the current package. (If name has the form `<xyx>` the Java implementation type is named `xyz`.) If `define-class` the implementation is unspecified. In most cases, the compiler creates a class pair, consisting of a Java interface and a Java implementation class. The class inherits from the classes and interfaces listed in supers. This is a list of names of classes that are in scope (perhaps imported using `require`), or names for existing classes or interfaces surrounded by `<>`, such as `<gnu.lists.Sequence>`. If `define-simple-class` is used, at most one of these may be the name of a normal Java class or classes defined using `define-simple-class`; the rest must be interfaces or classes defined using `define-class`. If `define-class` is used, all of the classes listed in supers should be interfaces or classes defined using `define-class`. Each field-decl declares a public instance "slot" (field) with the given fname. If ftype is specified it is the type of the slot. The following option-keywords are implemented: `type:` ftype Specifies that ftype is the type of (the values of) the field. Equivalent to `::` ftype``. `allocation:` `class:` Specifies that there should be a single slot shared between all instances of the class (and its sub-classes). Not yet implemented for `define-class`, only for `define-simple-class`. In Java terms this is a `static` field. `allocation:` `instance:` Specifies that each instance has a separate value "slot", and they are not shared. In Java terms, this is a non-`static` field. This is the default. `init-form:` expr An expression used to initialize the slot. The lexical environment of the expr is that of the `define-class` or `define-simple-class`. (This is not quite true in the current implementation, as the names of fields and methods of this class are visible.) `init-value:` value A value expression used to initialize the slot. For now this is synonymous with init-form:, but that may change (depending on what other implementation do), so to be safe only use `init-value:` with a literal. `init-keyword:` ``name`:` A keyword that that can be used to initialize instance in `make` calls. For now, this is ignored, and name should be the same as the field's fname. `static` field. Each method-decl declares a public non-static method, whose name is method-name. (If method-name is not a valid Java method name, it is mapped to something reasonable. For example `foo-bar?` is mapped to `isFooBar`.) The types of the method arguments can be specified in the formal-arguments. The return type can be specified by rtype, or is otherwise the type of the body. Currently, the formal-arguments cannot contain optional, rest, or keyword parameters. (The plan is to allow optional parameters, implemented using multiple overloaded methods.) The scope of the body of a method includes the field-decls of the object. It does include the surrounding lexical scope. It sort-of also includes the declared methods, but this is not working yet.

A simple example:

         (define-simple-class <2d-vector> ()
           (x type: <double> init-value: 0.0 init-keyword: x:)
           (y type: <double> init-value: 0.0 init-keyword: y:)
           ((add (other :: <2d-vector>)) :: <2d-vector>
            ;; Kawa compiles this using primitive Java types!
            (make <2d-vector>
              x: (+ x (slot-ref other 'x))
              y: (+ y (slot-ref other 'y))))
           ((scale (factor :: <double>)) :: <2d-vector>
            ;; Unfortunately, multiply is not yet optimized as addition is.
            (make <2d-vector> x: (* factor x) y: (* factor y))))

         (define-simple-class <3d-vector> (<2d-vector>)
           (z type: <double> init-value: 0.0 init-keyword: z:)
           ((scale (factor :: <double>)) :: <2d-vector>
            ;; Note we cannot override the return type to <3d-vector>
            ;; because Java does not allow that.  Should hide that. .
            (make <3d-vector>
              ;; Unfortunately, slot names of inherited classes are not visible.
              ;; Until this is fixed, use slot-ref.
              x: (* factor (slot-ref (this) 'x))
              y: (* factor (slot-ref (this) 'y))
              z: (* factor z))))