Last time: Discussed classes, objects, methods, subclass, subtypes, dynamic method invocation.
"And", "or" are non-short-circuit. Use "and then", "or else" for short circuit.
Note that all variables should be thought of as references to objects.
Can't do anything to a variable until you create it or assign a value to it.
(Illegal to send a message to an uninitialized variable.)
Thus a := b means that a now refers to the same object as b
Important:: Note that only local variables or own attributes may be assigned to or created:
If "a" has been created, a.copy(b) will change "a" to be field-by-field identical to "b" (using sharing for reference fields). It will crash if a = Void.
To give "a" a new copy of object referred to by "b", write a := clone(b)
Note that you need not have created a before you can do this!
deep_clone and deep_copy are similar, but copy recursively.
frozen equal(some: ANY; other: like some): BOOLEAN is ... ensure Result = (some = Void and other = Void) or (some /= Void and other /= Void and then some.is_equal(other) )The standard is_equal determines if fields which are references are identical (or flat fields are equal). If a "deep" version is desired, it may be programmed by hand or you may simply use deep_equal (some, other).
Note that clone and equal are routines available in any class.
They are defined in class ANY which has features which are available to all other classes. ANY also includes io which was used earlier.
Leads to necessity of resolving name clashes.
As a result the INHERIT clause has a number of options (which must occur in the following order:
rename -- allows the programmer to change the name of inherited features, can be especially helpful if get name clashes in multiple inheritance
export -- allows the user to change the export status of inherited features
undefine -- can be used to resolve name clashes in multiple inheritance
redefine -- warns that inherited feature will be redefined
select -- used to determine which method is to be used if there are two methods with same name (usually a result of multiple inheritance)
See chapters 6 and 7 in Switzer for details on how to use these.
Few tips: Suppose feature m is defined in class A and have
class B inherit A rename m as k end; feature ...
Now suppose that x : A, but at run time x actually holds a value of type B.
I.e., static type of x is A, but dynamic type is B.
By static type-checking, x.m should be defined. What is actually executed?
Answer: method k of B (only reasonable answer)
More complicated: Suppose you wish to redefine m in B, but use old definition in A as part of code:
class B inherit A rename m as old_m redefine m end; feature m (...) is do ... old_m ... end;
Unfortunately, this won't quite work. The problem arises again when we have a variable of static type A, holding a value of type B. What happens when x.m is executed?
As in the earlier example, the renamed version of m (in this case old_m) will be executed. This is not what we wanted. Instead must write:
class B inherit A rename m as old_m inherit A redefine m select m end; feature m (...) is do ... old_m ... end;
Here we have actually inherited m twice. Since there are two definitions which can be used when this object is held in a variable of type A, we must tell the system which to use. The select clause tells it to resolve the ambiguity by taking the m from the second version (which is redefined in the class!).
Ugly, but it resolves all of the ambiguities!
I personally think multiple inheritance is usually a bad idea and should generally be avoided. (See comments later on multiple subtyping - which is helpful!)
Assertions can be labelled - provides information in error messages and helpful in exception handling.
If method is redefined in subclass preconditions must be weaker (i.e., accept at least as many inputs as before) and postconditions must be stronger.
Therefore if use subclass in place of superclass, redefined method will accept any inputs that original would have and return results meeting expected criteria.
Go over parsing example on-line.