update-instance-for-different-class
update-instance-for-different-class Standard Generic Function
Syntax:
update-instance-for-different-class previous current &rest initargs &key &allow-other-keys → implementation-dependent
Method Signatures:
update-instance-for-different-class (previous standard-object)
(current standard-object)
&rest initargs
Arguments and Values:
previous—a copy of the original instance.
current—the original instance (altered).
initargs—an initialization argument list.
Description:
The generic function update-instance-for-different-class is not intended to be called by program mers. Programmers may write methods for it. The function update-instance-for-different-class is called only by the function change-class.
The system-supplied primary method on update-instance-for-different-class checks the validity of initargs and signals an error if an initarg is supplied that is not declared as valid. This method then initializes slots with values according to the initargs, and initializes the newly added slots with values according to their :initform forms. It does this by calling the generic function shared-initialize with the following arguments: the instance (current), a list of names of the newly added slots, and the initargs it received. Newly added slots are those local slots for which no slot of the same name exists in the previous class.
Methods for update-instance-for-different-class can be defined to specify actions to be taken when an instance is updated. If only after methods for update-instance-for-different-class are defined, they will be run after the system-supplied primary method for initialization and therefore will not interfere with the default behavior of update-instance-for-different-class.
Methods on update-instance-for-different-class can be defined to initialize slots differently from change-class. The default behavior of change-class is described in Section 7.2 (Changing the Class of an Instance).
The arguments to update-instance-for-different-class are computed by change-class. When change-class is invoked on an instance, a copy of that instance is made; change-class then de structively alters the original instance. The first argument to update-instance-for-different-class, previous, is that copy; it holds the old slot values temporarily. This argument has dynamic extent within change-class; if it is referenced in any way once update-instance-for-different-class returns, the results are undefined. The second argument to update-instance-for-different-class, current, is the altered original instance. The intended use of previous is to extract old slot values by using slot-value or with-slots or by invoking a reader generic function, or to run other methods that were applicable to instances of the original class.
Examples:
See the example for the *function* **change-class**.
Exceptional Situations:
The system-supplied primary method on update-instance-for-different-class signals an error if an initialization argument is supplied that is not declared as valid.
See Also:
change-class, shared-initialize, Section 7.2 (Changing the Class of an Instance), Section 7.1.4 (Rules for Initialization Arguments), Section 7.1.2 (Declaring the Validity of Initialization Arguments)
Notes:
Initargs are declared as valid by using the :initarg option to defclass, or by defining methods for update-instance-for-different-class or shared-initialize. The keyword name of each keyword parameter specifier in the lambda list of any method defined on update-instance-for-different-class or shared-initialize is declared as a valid initarg name for all classes for which that method is applicable.
The value returned by update-instance-for-different-class is ignored by change-class.
Expanded Reference: update-instance-for-different-class
Customizing Class Change Behavior
update-instance-for-different-class is called by change-class after the instance has been transformed. It receives a copy of the old instance (previous) and the modified instance (current). Define :before or :after methods to customize the transition.
(defclass cartesian-point ()
((x :initarg :x :accessor point-x)
(y :initarg :y :accessor point-y)))
(defclass polar-point ()
((radius :accessor point-radius)
(angle :accessor point-angle)))
(defmethod update-instance-for-different-class
:before ((old cartesian-point) (new polar-point) &key)
(let ((x (point-x old))
(y (point-y old)))
(setf (point-radius new) (sqrt (+ (* x x) (* y y))))
(setf (point-angle new) (atan y x))))
(let ((p (make-instance 'cartesian-point :x 3.0 :y 4.0)))
(change-class p 'polar-point)
(list (point-radius p) (point-angle p)))
=> (5.0 0.9272952)
The previous and current Arguments
The previous argument is a snapshot of the old state. The current argument is the actual (now transformed) instance. Use previous to read old slot values and current to set new ones.
(defclass employee ()
((name :initarg :name :accessor emp-name)
(salary :initarg :salary :accessor emp-salary)))
(defclass consultant ()
((name :initarg :name :accessor consultant-name)
(hourly-rate :accessor consultant-rate)))
(defmethod update-instance-for-different-class
:before ((old employee) (new consultant) &key)
;; Derive hourly rate from annual salary
(setf (consultant-rate new) (/ (emp-salary old) 2080.0)))
(let ((e (make-instance 'employee :name "Bob" :salary 104000)))
(change-class e 'consultant)
(list (consultant-name e) (consultant-rate e)))
=> ("Bob" 50.0)
Passing Initargs Through change-class
Initargs passed to change-class are forwarded to update-instance-for-different-class.
(defclass basic-user ()
((username :initarg :username :accessor user-name)))
(defclass admin-user ()
((username :initarg :username :accessor user-name)
(level :initarg :level :accessor admin-level :initform 1)))
(let ((u (make-instance 'basic-user :username "alice")))
(change-class u 'admin-user :level 5)
(list (user-name u) (admin-level u)))
=> ("alice" 5)