Next: Accessing object fields, Previous: Calling Java methods from Scheme, Up: Object, Classes and Modules [Contents][Index]
The recommended way to create an instance of a type T
is to “call” T as if it were a function, with the
arguments used to initialize the object.
If T
is a class and T
has a matching constructor,
then the arguments will used for constructor arguments:
(java.util.StringTokenizer "this/is/a/test" "/")
(You can think of the type T as being coerced to an instance-constructor function.)
If T
is a container or collection type,
then typically the arguments will be used to specify
the child or component values.
Many standard Scheme procedures fit this convention.
For example in Kawa list
and vector
evaluate to
types, rather than procedures as in standard Scheme,
but because types can be used as constructor functions it just works:
(list 'a (+ 3 4) 'c) ⇒ (a 7 c) (vector 'a 'b 'c) ⇒ #(a b c)
Any class T
that has a default constructor
and an add
method can be initialized this way.
Examples are java.util
collection classes,
and jawa.awt
and javax.swing
containers.
(java.util.ArrayList 11 22 33) ⇒ [11, 22, 333]
The above expression is equivalent to:
(let ((tmp (java.util.ArrayList))) (tmp:add 11) (tmp:add 22) (tmp:add 33) tmp)
Allocating Java arrays (see Creating-new-Java-arrays) uses a similar pattern:
(define iarr (int[] 2 3 5 7 11))
A value argument can use a “splice operator” @expression
to “flatten” a generalized sequence;
this is a special case of argument list handling.
This example converts one type of sequence (Java-array of int
)
into another (Java-array of float
):
(define farr (float[] @iarr))
Sometimes you want to set some named property to an initial value. You can do that using a keyword argument. For example:
(javax.swing.JButton text: "Do it!" tool-tip-text: "do it")
This is equivalent to using setter methods:
(let ((tmp (javax.swing.JButton))) (tmp:setText "Do it!") (tmp:setToolTipText "do it") tmp)
A keyword argument key-name
:
can
can translated to either a
or a set
KeyName:
method.
The latter makes it convenient to add listeners:
add
KeyName:
(javax.swing.JButton text: "Do it!" action-listener: (object (java.awt.event.ActionListener) ((actionPerformed e) (do-the-action))))
This is equivalent to:
(let ((tmp (javax.swing.JButton))) (tmp:setText "Do it!") (tmp:addActionListener (object (java.awt.event.ActionListener) ((actionPerformed e) (do-the-action)))) tmp)
Making use of so-called “SAM-conversion” (see SAM-conversion) makes it even more convenient:
(javax.swing.JButton text: "Do it!" action-listener: (lambda (e) (do-the-action)))
The general case allows for a mix of constructor arguments, property keywords, and child values:
class-type constructor-value... property-initializer... child-value... constructor-value ::= expression property-initializer ::= keyword expression child-value ::= expression
First an object is constructed with the constructor-value arguments (if any) passed to the object constructor; then named properties (if any) are used to initialize named properties; and then remaining arguments are used to add child values.
There is an ambiguity if there is no property-initializer - we can’t distinguish between a constructor-value and a child-value. In that case, if there is a matching constructor method, then all of the arguments are constructor arguments; otherwise, there must a default constructor, and all of the arguments are child-value arguments.
There is a trick you can you if you need both
constructor-value and child-value arguments:
separate them with an “empty keyword” ||:
.
This matches a method named add
, which means that
the next argument effectively a child-value - as do
all the remaining arguments. Example:
(let ((vec #(1 2 3))) (java.util.ArrayList vec ||: 4 5 6)) ⇒ [1, 2, 3, 4, 5, 6]
The compiler rewrites these allocations expression to generated efficient bytecode, assuming that the “function” being applied is a type known by the compiler. Most of the above expressions also work if the type is applied at run-time, in which case Kawa has to use slower reflection:
(define iarr int[]) (apply iarr (list 3 4 5)) ⇒ [3 4 5]
However addXxx
methods and SAM-conversion
are currently only recognized in the case of a class known at compile-time,
not at run-time.
Here is a working Swing demo illustrating many of these techniques:
(import (class javax.swing JButton Box JFrame)) (define-simple-class HBox (Box) ((*init*) (invoke-special Box (this) '*init* 0))) (define value 0) (define txt (javax.swing.JLabel text: "0")) (define (set-value i) (set! value i) (set! txt:text (number->string i))) (define fr (JFrame title: "Hello!" (Box 1#|VERTICAL|# ||: (javax.swing.Box:createGlue) txt (javax.swing.Box:createGlue) (HBox (JButton ;; uses 1-argument constructor "Decrement" ;; constructor argument tool-tip-text: "decrement" action-listener: (lambda (e) (set-value (- value 1)))) (javax.swing.Box:createGlue) (JButton ;; uses 0-argument constructor text: "Increment" tool-tip-text: "increment" action-listener: (lambda (e) (set-value (+ value 1)))))))) (fr:setSize 200 100) (set! fr:visible #t)
If you prefer, you can use the older make
special function:
Constructs a new object instance of the specified type,
which must be either a java.lang.Class
or a
<gnu.bytecode.ClassType>
.
Equivalent to:
type args ...
Another (semi-deprecated) function is to use the colon notation
with the new
pseudo-function.
The following three are all equivalent:
(java.awt.Point:new x: 4 y: 3) (make java.awt.Point: x: 4 y: 3) (java.awt.Point x: 4 y: 3)
Next: Accessing object fields, Previous: Calling Java methods from Scheme, Up: Object, Classes and Modules [Contents][Index]