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.
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.)
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
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)
T that has a default constructor
add method can be initialized this way.
java.util collection classes,
(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:
(int 2 3 5 7 11)
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
can translated to either a
The latter makes it convenient to add listeners:
(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]
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
type args ...
Another (semi-deprecated) function is to use the colon notation
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)