Go to the first, previous, next, last section, table of contents.


Object-oriented features

Kawa provides various ways to define, create, and access Java objects. Here are the currently supported features.

Records

The Record package provides a facility for users to define their own record data types. Records are extensions of the class Record. These procedures use the Java 1.1 reflection facility.

Function: make-record-type type-name field-names
Returns a record-type descriptor, a value representing a new data type disjoint from all others. The type-name argument must be a string, but is only used for debugging purposes (such as the printed representation of a record of the new type). The field-names argument is a list of symbols naming the fields of a record of the new type. It is an error if the list contains any duplicates.

Function: record-constructor rtd [field-names]
Returns a procedure for constructing new members of the type represented by rtd. The returned procedure accepts exactly as many arguments as there are symbols in the given list, field-names; these are used, in order, as the initial values of those fields in a new record, which is returned by the constructor procedure. The values of any fields not named in that list are unspecified. The field-names argument defaults to the list of field names in the call to make-record-type that created the type represented by rtd; if the field-names argument is provided, it is an error if it contains any duplicates or any symbols not in the default list.

Function: record-predicate rtd
Returns a procedure for testing membership in the type represented by rtd. The returned procedure accepts exactly one argument and returns a true value if the argument is a member of the indicated record type; it returns a false value otherwise.

Function: record-accessor rtd field-name
Returns a procedure for reading the value of a particular field of a member of the type represented by rtd. The returned procedure accepts exactly one argument which must be a record of the appropriate type; it returns the current value of the field named by the symbol field-name in that record. The symbol field-name must be a member of the list of field-names in the call to make-record-type that created the type represented by rtd.

Function: record-modifier rtd field-name
Returns a procedure for writing the value of a particular field of a member of the type represented by rtd. The returned procedure accepts exactly two arguments: first, a record of the appropriate type, and second, an arbitrary Scheme value; it modifies the field named by the symbol field-name in that record to contain the given value. The returned value of the modifier procedure is unspecified. The symbol field-name must be a member of the list of field-names in the call to make-record-type that created the type represented by rtd.

Function: record? obj
Returns a true value if obj is a record of any type and a false value otherwise.

Function: record-type-descriptor record
Returns a record-type descriptor representing the type of the given record. That is, for example, if the returned descriptor were passed to record-predicate, the resulting predicate would return a true value when passed the given record.

Function: record-type-name rtd
Returns the type-name associated with the type represented by rtd. The returned value is eqv? to the type-name argument given in the call to make-record-type that created the type represented by rtd.

Function: record-type-field-names rtd
Returns a list of the symbols naming the fields in members of the type represented by rtd. The returned value is equal? to the field-names argument given in the call to make-record-type that created the type represented by rtd.

Allocating objects

Function: make type args ...
Constructs a new object instance of the specified type, which must be either a java.lang.Class or a <gnu.bytecode.ClassType>.

The args ... are passed to the constructor of the class type. If there is no applicable constructor, and the args ... consist of a set of (keyword,value)-pair, then the default constructor is called, and each (keyword,value)-pair is used to set the correspdong slot of the result, as if by: (slot-set! result keyword value).

For example, the following are all equivalent:

(set! p (make <java.awt.Point> 3 4))

(set! p (make <java.awt.Point> y: 4 x: 3))

(set! p (make <java.awt.Point>))
(slot-set! p 'x 3)
(set! (slot-ref p 'y) 4)

Accessing fields of Java objects

Kawa has both a high-level interface and a low-level interface for accessing the fields of Java objects and static fields. The lower-level interfaces are macros that return functions. These functions can be inlined, producing efficient code. The higher-level functions are less verbose and more convenient. However, they can only access public fields, and they use the slower Java reflection facility. (A possible future optimization might be for the compiler to inline direct field accesses when it knows that the object's class.)

High-level operations on Object Fields

Function: field object fieldname
Get the instance field with the given fieldname from the given Object. Returns the value of the field, which must be public. This procedure has a setter, and so can be used as the first operand to set!.

If name is not a valid Java name, it is "mangled" into a Java name: For example, "file-name" is mangled to "fileName".

If there is no accessible field whose name is "fieldname", we look for a no-argument method whose name is "getFieldname".

Function: static-field class fieldname
Get the static field with the given fieldname from the given class. Returns the value of the field, which must be public. This procedure has a setter, and so can be used as the first operand to set!.

Examples:

(static-field <java.lang.System> 'err)
;; Copy the car field of b into a.
(set! (field a 'car) (field b 'car))

Function: slot-ref object fieldname
A synonym for (field object fieldname).

Low-level Operations on Object Fields

The following macros evaluate to procedures that can be used to access or change the fields of objects or static fields. The compiler can inline each to a single bytecode instruction (not counting type conversion).

Syntax: primitive-get-field class fname ftype
Use this to access a field named fname having type type in class class. Evaluates to a new one-argument procedure, whose argument is a reference to an object of the specified class. Calling that procedure returns the value of the specified field.

Syntax: primitive-set-field class fname ftype
Use this to change a field named fname having type type in class class. Evaluates to a new two-argument procedure, whose first argument is a reference to an object of the specified class, and the second argument is the new value. Calling that procedure sets the field to the specified value. (This macro's name does not end in a `!', because it does not actually set the field. Rather, it returns a function for setting the field.)

Syntax: primitive-get-static class fname ftype
Like primitive-get-field, but used to access static fields. Returns a zero-argument function, which when called returns the value of the static field.

Syntax: primitive-set-static class fname ftype
Like primitive-set-field, but used to modify static fields. Returns a one-argument function, which when called sets the value of the static field to the argument.

Calling Java methods from Scheme

Kawa has both a low-level and a high-level "Foreign Function Interface", which allows you to call any (virtual or static) Java method as if it were a Scheme procedure. The high-level one is incomplete, so I will stick to what is implemented so far.

Function: invoke-static class name args ...
The class can be a <java.lang.Class>, a <gnu.bytecode.ClassType>, or a <symbol> or <string> that names a Java class. The name can be <symbol> or <string> that names one or more methods in the Java class.

If name is not a valid Java name, it is "mangled" into a Java name: For example, "open-file" is mangled to "openFile", and "char?" is mangled to "isChar".

Any public methods (static or instance) in the specified class (or its super-classes) that match "name" or "name$V" collectively form a generic procedure. When the procedure is applied to the argument list, the most specific applicable method is chosen depending on the argument list; that method is then called with the given arguments. Iff the method is an instance method, the first actual argument is used as the this argument. If there are no applicable methods (or no methods at all!), or there is no "best" method, WrongType is thrown.

("name$V" will be used for procedures with #!rest or keyword args; the last argument must be an array type; all the "extra" arguments must be compatible with the type of the array elements.)

An example (derived from the Skij FAQ):

(invoke-static <java.lang.Thread> 'sleep 100)

The behavior of interpreted code and compiled code is not indentical, though you should get the same result either way unless you have designed the classes rather strangely. The details will be nailed down later, but the basic idea is that the compiler will "inline" the invoke-static call if it can pick a single "best" matching method.

Function: class-methods class name
Return a generic function containing those methods of class that match the name name, in the sense of invoke-static. Same as:
(lambda args (apply invoke-static (cons class (cons name args))))

The following lower-level primitives require you to specify the parameter and return types. Type specifications are currently required to be string literals or one of the standard types (see section Standard Types). A type specifier can be a fully-qualified Java class name (for example <java.lang.StringBuffer>). In that case, the actual argument is cast at run time to the named class. The specification <String> is an exception: It causes the toString method of the actual argument to be invoked.

A type specifier can also be one of the primitive Java types. The numeric types <long>, <int>, <short>, <byte>, <float>, and <double> are converted from the corresponding Scheme number classes. Similarly, <char> can be converted to and from Scheme characters. The type boolean matches any object, and the result is false if and only if the actual argument is #f. The return type <void> indicates that no value is returned.

Syntax: primitive-constructor class (argtype ...)
Returns a new anonymous procedure, which when called will create a new object of the specified class, and will then call the constructor matching the specified argument types.

Syntax: primitive-virtual-method class method rtype (argtype ...)
Returns a new anonymous procedure, which when called will invoke the instance method whose name is the string method in the class whose name is class.

Syntax: primitive-static-method class method rtype (argtype ...)
Returns a new anonymous procedure, which when called will invoke the static method whose name is the string method in the class whose name is class.

Syntax: primitive-interface-method interface method rtype (argtype ...)
Returns a new anonymous procedure, which when called will invoke the matching method from the interface whose name is interface.

The macros return procedure values, just like lambda. If the macros are used directly as the procedure of a procedure call, then kawa can inline the correct bytecodes to call the specified methods. (Note also that neither macro checks that there really is a method that matches the specification.) Otherwise, the Java reflection facility is used.

Some examples using these primitives are `vectors.scm' and `characters.scm' the directory `kawa/lib' in the Kawa sources.

Anonymous classes

Syntax: object (supers ...) field-or-method-decl ...
Returns a new instance of an anonymous (inner) class.
field-or-method = field-decl | method-decl
field-decl = (field-name [[field-type] field-init])
method-decl = ((method-name formal-arguments) [rtype] body)

Returns a new instance of a unique (anonymous) class. The class inherits from the list of supers, where at most one of the elements should be the base class being extended from, and the rest are interfaces.

Each field-decl declares a public instance field. If field-type is given, it specifies the type of the field. If field-init is given, it is an expression whose value becomes the initial value of the field. The field-init is evaluated at the same time as the object expression is evaluated, in a scope where all the field-names are visible.

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.


Go to the first, previous, next, last section, table of contents.