Any constraints necessary on arguments and result types of redefined methods when we wish only *type correctness to be preserved* ? class C { function m(s:S): T is { .... } function n(anS:S): U is { ... self <= m(anS) ... } } class SC inherits C modifies m { function m(s:S’): T’ is // For which S’, T’ safe? { ... } } ansc <= n in n's body: self <= m(anS) if we invoke n on an object of SC, the redefined m will be run so we need S'-> T' <: S -> T hence S <: S' Changing argument types (contravariantly) seems useless in actual examples Some powerful constructs build subclasses without subtypes nonetheless types of redefinded methods need to be subtypes Changing instance variable types in subclasses NOT possible! Recall discussion on reference types. There should be a covariant change if looked at as values (r-value) a contravariant change if looked at as location (l-value) class Rect { ul:Point := nil; // upper left corner lr:Point := nil; // lower right corner function setUL(newUL:Point): Void is { self.ul := newUL } } class ColorRect { ul:ColorPoint := nil; lr:ColorPoint := nil; ... } Subclass ColorRect redefines types of instance variables. BUT setUL would assign a Point to a ColorPoint variable!!! Java solution: The new instance variable are actually * new instance variables with the same name * ============================== Onother sort of change in subclasses: methods' visibility Showing hidden methods in subclasses is sound w.r.t. the subtype rule for object types The opposite NOT!! May we do the reverse? Yes, of course. if we do not mind subclasses producing subtypes... (why?) Look at the type of an object in a subclass changing a method from public to protected. public (for all, public!) protected (just for objects in class and subclasses) hidden (just for object in class) public --> protected no problem protected --> private no problem beware when it is overridden in subsubclasses All restrictions of visibility possible ... if we do not care about subtyping! ======================================================== Non Class-based Object Oriented Programming Languages Object-based languages In most object-based languages, objects can be constructed directly by means of appropriate used-define functions. Example: function makePoint(nx:Integer,ny:Integer): PointType is { return object { x: Integer := nx; y: Integer := ny; function move(dx: Integer,dy:Integer): Void is { x := x + dx; y := y + dy; } } } makePoint produces objects all with the same shape. ---------------------------------- A special kind of object-based languages: Prototype-based languages - Notion of "prototype" - Use of cloning - Updating features - (Usually) dynamic updating also of methods If everything is updatable, no distinction between variables and methods (both called "term features") Features can be "added" by Extension o extends p ===> o has separate copies of p's fields Delegation o delegates to p ===> p's fields in o are still those of p Thus an object created using delegation retains ties to its prototype parent, while an object created by extension is independent of its parent. Object-based advantages: simpler sintax, flexibility (dynamic updates!) dynamic updates ===> semantic complexity Foundation for object-based languages: Object calculus [Abadi, Cardelli] -------------------------------------- Another kind of non class-based languages Multi-method Languages No receivers of messages Method calls like procedure calls BUT method body to be executed depends on run-time types function equal(p1:Point,p2:Point): Boolean is { return p1.x = p2.x & p1.y = p2.y } function equal(p1:ColorPoint,p2:ColorPoint): Boolean is { return p1.x = p2.x & p1.y = p2.y & p1.color = p2.color } ColorPoint <: Point pt1, pt2: Point If run-time type is Point, first equal If run-time is Color-Point, second equal - No receiver - body selection made at run time (dynamic method overloading) An overloding different from overloading in Java (static) In example: what if equal(pt1, pt2) with pt1 is Point and pt2 is ColorPoint at run-time? A Point cannot be a ColorPoint, hence... Chioce depending on the "better match", for instance what pt1 and pt2 both are ColorPoint Not always easy! Example: function compare(p1:Point, p2: ColorPoint): Boolean is { return ... } function compare(p1:ColorPoint, p2:Point): Boolean is { return ... } compare(pt1, pt2) no type-safe when both Point ambiguity when both ColorPoint CLOS: rules provided to specify how to disambiguate Cecil: programmer must provide other methods to get out of ambiguity (similar to java for overloaded methods statically ambiguous) Multi-methods: no problems with binary methods!!! No meaning for "self"! Problems about encapsulation and information hiding Single-dispatch languages: method lookup among methods of the receiver (those allowed to access non-public features) Multi-method languages: all methods with the same name logically belongs together (in terms of lookup strategy) contains(aPoint, aCircle) may need non-public features of both Point and Circle Which class does contains belong to? Result: problems with modularity!! multi-methods languages really Object-Oriented? ============================== Some O.O.languages *Simula 67* Extension of Algol 60 Notions of object, class, inheritance, dynamic method invocation No information hiding (neither for instance variables, nor methods) *Beta* Successor of SImula 67 Beta and Simula: use of "inner" for method overriding Invocation of everridden method: code in superclass before inner code of subclass code in superclass after inner Beta: use of virtual classes Example (it uses Bruce's notation) class Cell { deftype A <: Object; value: A := null; function set(v:A): Void is { value := v } function get(): A is { return value } } class StringCell inherits Cell { deftype A <: String; ... } 'A' is not a type parameter!!! Just to specify that it can be specialized in subclasses StringCell "subtype" of Cell so... type problems because of covariant change in set! Run-time type checks added! We could us polymorphic classes instead of Virtual types NOT always!! ObjectType Observer { deftype S <: Subject; deftype E <: Event; function notify(s:S, e: E): Void; } ObjectType Subject { deftype O <: Observer; deftype E <: Event; function register(s:S): Void; function notifyObservers(e:E): Void; } class ObserverClass { deftype S <: Subject; deftype E <: Event; function notify(s:S, e: E): Void is { ... } } class SubjectClass { deftype O <: Observer; deftype E <: Event; observers:Array of O; function register(o:O): Void is { ... } function notifyObservers(e:E): Void is { ... observers[i].notify(self,e) ... } } ObjectType WindowObserver { ... } ObjectType WindowSubject { ... } class WindowObserverClass { deftype S <: WindowSubject; deftype E <: WindowEvent; function notify(s:S, e:E): Void is // override { super.notify(s,e); ... s.windowMeth() ... } } class WindowSubjectClass { deftype O <: WindowObserver; deftype E <: Event; function windowMeth(...): ... is // new method { ... } } ---------------- * C++ and Java * C++ both O.O. and procedural Java only O.O. C++ and Java originally invariant C++ now covatiant changes in return types C++ multiple inheritance Java single inheritance but multiple for interfaces (hence object types) C++ and Java: Subtyping determined by declaration Java: not fully supporting subtyping bexp?texp:fexp type checking requires types texp and fexp to be the same or one extends the other bexp?pt:pt OK bexp?pt:cpt OK bexp?fpt:pt OK bexp?fpt:cpt NO!! fpt and cpt have an upper bound but not necessarily a least one Fig.7.4 *Smalltalk* dynamically typed *Eiffel* use of "like Current" (good for binary methods) not sound w.r.t. static typing Sather extended Eiffel fixing subtype problems (controvariant changes in patameter types; no changes in instance variables)