How would you express the following Java code in Lisp?
class Foo {
private String s;
public Foo(String s) {
this.s = s;
}
}
class Bar extends Foo {
public Bar(int i) {
super(Integer.toString(i));
}
}
In Lisp, is make-instance
or initialize-instance
the equivalent of a constructor? If yes, how do you call the super class constructor(s)?
A constructor is a special method of a class or structure in object-oriented programming that initializes a newly created object of that type. Whenever an object is created, the constructor is called automatically.
A class constructor is a special member function of a class that is executed whenever we create new objects of that class. A constructor will have exact same name as the class and it does not have any return type at all, not even void.
A Constructor is a block of code that initializes a newly created object. A Method is a collection of statements which returns a value upon its execution. A Constructor can be used to initialize an object. A Method consists of Java code to be executed.
Abstract class cannot have a constructor. Explanation: No instance can be created of abstract class.
Use CALL-NEXT-METHOD
from within a method to call the next one.
Typically just use the standard method combination facility and write an :BEFORE
, :AFTER
or :AROUND
method for INITIALIZE-INSTANCE
.
One way:
(defclass foo ()
((s :type string :initarg :s)))
(defclass bar (foo) ())
(defmethod initialize-instance :around ((b bar) &key i)
(call-next-method b :s (princ-to-string i)))
Example:
CL-USER 17 > (make-instance 'bar :i 10)
#<BAR 402000B95B>
CL-USER 18 > (describe *)
#<BAR 4130439703> is a BAR
S "10"
Note that I've called CALL-NEXT-METHOD
without changing the argument it dispatches on. I just changed the &key
initarg parameter.
Here is an amplification of Rainier's example that adds a small twist: specialization (subclassing) by constraining slot values, at least at construction time. Consider a class, m-box, for two-dimensional rectangles:
(defclass m-box ()
((left :accessor m-box-left :initform 0 :type integer :initarg :left )
(top :accessor m-box-top :initform 0 :type integer :initarg :top )
(width :accessor m-box-width :initform 0 :type integer :initarg :width )
(height :accessor m-box-height :initform 0 :type integer :initarg :height)))
We can try it like this:
(describe (make-instance 'm-box :left 42 :top -39 :width 5 :height 11))
: #<M-BOX {10047A8F83}>
: [standard-object]
:
: Slots with :INSTANCE allocation:
: LEFT = 42
: TOP = -39
: WIDTH = 5
: HEIGHT = 11
Now consider a subclass or specialization: let an m-block be an m-box with unit width and height. We set the initialize-instance method to pass through values for left and top, but not width and height:
(defclass m-block (m-box) ())
(defmethod initialize-instance
:around
((mb m-block)
&key (left 0) (top 0))
(call-next-method mb :left left :top top :width 1 :height 1))
We can make an instance of m-block as follows:
(describe (make-instance 'm-block :left 17 :top -34 :width 5 :height 11))
: #<M-BLOCK {10049C0CC3}>
: [standard-object]
:
: Slots with :INSTANCE allocation:
: LEFT = 17
: TOP = -34
: WIDTH = 1
: HEIGHT = 1
The constructor does not block the user's attempt to set width and height, as it would do with some non-existent slot:
(describe (make-instance 'm-block :left 17 :top -34 :plugh 2345))
Invalid initialization argument:
:PLUGH
in call for class #<STANDARD-CLASS COMMON-LISP-USER::M-BLOCK>.
[Condition of type SB-PCL::INITARG-ERROR]
but the constructor does correct the user's invalid inputs with 1.
You might want to make the model more watertight by calling error
if the
user attempts to input invalid width or height:
(defclass m-block (m-box) ())
(defmethod initialize-instance
:around
((mb m-block)
&key (left 0) (top 0) (width 1) (height 1))
(when (or (/= 1 width) (/= 1 height))
(error "an m-block must have unit width and height"))
(call-next-method mb :left left :top top :width 1 :height 1))
The following attempt by the user is rejected:
(describe (make-instance 'm-block :left 17 :top -34 :width 5 :height 11))
an m-block must have unit width and height
[Condition of type SIMPLE-ERROR]
But this one, which also demonstrates defaulting of height, goes through:
(describe (make-instance 'm-block :left 17 :top -34 :width 1))
: #<M-BLOCK {1005377983}>
: [standard-object]
:
: Slots with :INSTANCE allocation:
: LEFT = 17
: TOP = -34
: WIDTH = 1
: HEIGHT = 1
This example allows the user to setf
the width or height afterwards. I do not
know how to make the width and height read-only in instances of the subclass
and not in instances of the superclass.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With