Inheritance, Types Prototype-based, typically JavaScript – “Subtyping” function Type() { } Type.prototype.foo = function foo() { throw new Error("This is an abstract method!"); var type = }; new Type(); Type.prototype.bar = function bar() { type.foo(); throw new Error("This is an abstract method!"); type.bar(); }; function SubType() { } SubType.prototype = new Type(); SubType.prototype.constructor = SubType; SubType.prototype.foo = function foo() { document.write("foo()<br>"); var subtype = }; new SubType(); SubType.prototype.bar = function bar() { subtype.foo(); document.write("bar()<br>"); subtype.bar(); }; 32/128
Inheritance, Types Reuse and subtyping combined function AbstractMeasure() { } AbstractMeasure.prototype.getValue = function getValue() { document.write("Some code before...<br>"); return this .getRealValue(); }; AbstractMeasure.prototype.getRealValue = function getRealValue() { throw new Error("Abstract method!"); }; function Length() { } Length.prototype = new AbstractMeasure(); Length.prototype.constructor = Length; Length.prototype.getRealValue = function getRealValue() { document.write("Specific code.<br>"); return 1; }; 33/128
No real distinction between Inheritance, Types reuse and “subtyping” Reuse and subtyping combined function AbstractMeasure() { } AbstractMeasure.prototype.getValue = function getValue() { document.write("Some code before...<br>"); return this .getRealValue(); }; AbstractMeasure.prototype.getRealValue = function getRealValue() { throw new Error("Abstract method!"); }; function Length() { } Length.prototype = new AbstractMeasure(); Length.prototype.constructor = Length; Length.prototype.getRealValue = function getRealValue() { document.write("Specific code.<br>"); return 1; }; 34/128
Inheritance, Types Java vs. JavaScript – Make no mistake: JavaScript is no less “powerful” or “clean” than Java! – Different paradigms • Class + Imperative + Strongly-typed • Prototype + Functional + Dynamically-typed – Different implementations of the paradigms • Visibility • Pairs name + value 35/128
Polymorphism Problem: change behaviour of concrete objects Solution: polymorphism Definition – Subtype polymorphism, almost universally called polymorphism in object-oriented programming – The ability to create a variable, a function, or an object that has more than one form – Superset of overloading, overriding 36/128
Overriding Definition – A language feature that allows a subclass or a child class to provide a specific implementation of a method that is already provided by one of its super / parent classes 37/128
Overriding Class-based, typically Java public class SuperClass { public void foo() { System. out.println("One implementation."); } } public class SubClass extends SuperClass { public void foo() { super .foo(); System. out.println("Another implementation."); } } 38/128
Overriding Class-based, typically Java – Declared type vs. Concrete type – Overriding is resolved by the dynamic type of the receiver , not its static type public class Main { public static void main( final String[] args) throws InstantiationException, IllegalAccessException { final SuperClass superClass = new SubClass(); superClass.foo(); final SubClass subClass = new SubClass(); ((SuperClass) subClass).foo(); ((SuperClass) subClass.getClass().getSuperclass().newInstance()).foo(); } } 39/128
Distinction between declared type and Overriding concrete type Class-based, typically Java – Declared type vs. Concrete type – Overriding is resolved by the dynamic type of the receiver , not its static type public class Main { public static void main( final String[] args) throws InstantiationException, IllegalAccessException { final SuperClass superClass = new SubClass(); superClass.foo(); final SubClass subClass = new SubClass(); ((SuperClass) subClass).foo(); ((SuperClass) subClass.getClass().getSuperclass().newInstance()).foo(); } } 40/128
Distinction between declared type and Overriding concrete type Runtime dispatch Class-based, typically Java – Declared type vs. Concrete type – Overriding is resolved by the dynamic type of the receiver , not its static type public class Main { public static void main( final String[] args) throws InstantiationException, IllegalAccessException { final SuperClass superClass = new SubClass(); superClass.foo(); final SubClass subClass = new SubClass(); ((SuperClass) subClass).foo(); ((SuperClass) subClass.getClass().getSuperclass().newInstance()).foo(); } } 41/128
Distinction between declared type and Overriding concrete type Runtime dispatch Class-based, typically Java Reflection – Declared type vs. Concrete type – Overriding is resolved by the dynamic type of the receiver , not its static type public class Main { public static void main( final String[] args) throws InstantiationException, IllegalAccessException { final SuperClass superClass = new SubClass(); superClass.foo(); final SubClass subClass = new SubClass(); ((SuperClass) subClass).foo(); ((SuperClass) subClass.getClass().getSuperclass().newInstance()).foo(); } } 42/128
Overriding Prototype-based, typically JavaScript function SuperClass() { } SuperClass.prototype.foo = function foo() { document.write("One implementation...<br>"); }; function SubClass() { } SubClass.prototype = new SuperClass(); SubClass.prototype.constructor = SubClass; SubClass.prototype.parent = SuperClass.prototype; SubClass.prototype.foo = function foo() { this .parent.foo.call( this ); document.write("Another implementation...<br>"); }; – Declared type vs. concrete type var subClass = new SubClass(); subClass.foo(); 43/128
No type declaration Unlike Java! Overriding Prototype-based, typically JavaScript function SuperClass() { } SuperClass.prototype.foo = function foo() { document.write("One implementation...<br>"); }; function SubClass() { } SubClass.prototype = new SuperClass(); SubClass.prototype.constructor = SubClass; SubClass.prototype.parent = SuperClass.prototype; SubClass.prototype.foo = function foo() { this .parent.foo.call( this ); document.write("Another implementation...<br>"); }; – Declared type vs. concrete type var subClass = new SubClass(); subClass.foo(); 44/128
No type declaration Unlike Java! Overriding No super Unlike Java! Prototype-based, typically JavaScript function SuperClass() { } SuperClass.prototype.foo = function foo() { document.write("One implementation...<br>"); }; function SubClass() { } SubClass.prototype = new SuperClass(); SubClass.prototype.constructor = SubClass; SubClass.prototype.parent = SuperClass.prototype; SubClass.prototype.foo = function foo() { this .parent.foo.call( this ); document.write("Another implementation...<br>"); }; – Declared type vs. concrete type var subClass = new SubClass(); subClass.foo(); 45/128
Overriding Java vs. JavaScript – Make no mistake: JavaScript is no less “powerful” or “clean” than Java! – Different paradigms • Class + Imperative + Strongly-typed • Prototype + Functional + Dynamically-typed – Different implementations of the paradigms • Runtime method dispatch • Possibility to change the context of a call 46/128
Overloading Definition – Methods that have the same name but different signatures inside the same class or a class hierarchy 47/128
Overloading Class-based, typically Java public class Point { public boolean equals(final Object anObject) { System. out.println("One implementation."); return false; } public boolean equals(final Point aPoint) { System. out.println("Another implementation."); return false; } } public class Main { public static void main(final String[] args) { final Point p1 = new Point(); final Point p2 = new Point(); final Object o = p1; System. out.println(p1.equals(p2)); System. out.println(o.equals(p2)); System. out.println(p1.equals(o)); } } 48/128
Overloading Class-based, typically Java public class Point { public boolean equals(final Object anObject) { System. out.println("One implementation."); return false; } public boolean equals(final Point aPoint) { System. out.println("Another implementation."); return false; } } public class Main { public static void main(final String[] args) { final Point p1 = new Point(); Another implementation. final Point p2 = new Point(); false final Object o = p1; System. out.println(p1.equals(p2)); System. out.println(o.equals(p2)); System. out.println(p1.equals(o)); } } 49/128
Overloading Class-based, typically Java public class Point { public boolean equals(final Object anObject) { System. out.println("One implementation."); return false; } public boolean equals(final Point aPoint) { System. out.println("Another implementation."); return false; } } public class Main { public static void main(final String[] args) { final Point p1 = new Point(); Another implementation. final Point p2 = new Point(); false final Object o = p1; One implementation. System. out.println(p1.equals(p2)); false System. out.println(o.equals(p2)); System. out.println(p1.equals(o)); } } 50/128
Overloading Class-based, typically Java public class Point { public boolean equals(final Object anObject) { System. out.println("One implementation."); return false; } public boolean equals(final Point aPoint) { System. out.println("Another implementation."); return false; } } public class Main { public static void main(final String[] args) { final Point p1 = new Point(); Another implementation. final Point p2 = new Point(); false final Object o = p1; One implementation. System. out.println(p1.equals(p2)); false System. out.println(o.equals(p2)); One implementation. System. out.println(p1.equals(o)); false } } 51/128
Overloading Class-based, typically Java – The equals() method in Point takes a Point instead of an Object as an argument – It does not override equals in Object – It is just an overloaded alternative – Overloading is resolved by the static type of the argument , not its run-time type 52/128
Overloading Class-based, typically Java – Using the static type of the argument is natural in a strongly-typed language... – Else how could the program compile? final Point p1 = new Point(); final Object o = p1; System. out.println(o.equals(p2)); 53/128
// Compiled from Main.java (version 1.6 : 50.0, Overloading super bit) public class ca.concordial.soeb6461.overloading.Main { // Method descriptor #6 ()V // Stack: 1, Locals: 1 public Main(); Class-based, typically 0 aload_0 [this] Java 1 invokespecial java.lang.Object() 4 return – Using the static type of […] the argument is natural in a strongly-typed // Method descriptor #15 ([Ljava/lang/String;)V // Stack: 3, Locals: 4 language... public static void main(java.lang.String[] args); – Else how could the […] 32 aload_3 [o] program compile? 33 aload_2 [p2] 34 invokevirtual java.lang.Object.equals(java.lang.Object) : boolean final Point p1 = new Point(); 37 invokevirtual final Object o = p1; java.io.PrintStream.println(boolean) : void System. out.println(o.equals(p2)); 40 getstatic java.lang.System.out : java.io.PrintStream 54/128
Overloading Prototype-based, typically JavaScript – The issue here is really that JavaScript is dynamically-typed – Overloading just does not make sense ! 55/128
Overloading Prototype-based, typically JavaScript – Implementing the look-up table manually // addMethod - By John Resig (MIT Licensed) function addMethod(object, name, fn){ var old = object[ name ]; object[ name ] = function (){ if ( fn.length == arguments.length ) return fn.apply( this , arguments ); else if ( typeof old == 'function' ) return old.apply( this , arguments ); }; } When control enters the execution context of a function an arguments object is created. The arguments object has an array-like structure with an indexed property for each passed argument and a length property equal to the total number of parameters supplied by the caller. 56/128
Overloading Prototype-based, typically JavaScript – Implementing the look-up table manually function Users(){} addMethod(Users.prototype, "find", function(){ // Find all users... }); addMethod(Users.prototype, "find", function(name){ // Find a user by name }); addMethod(Users.prototype, "find", function(first, last){ // Find a user by first and last name }); var users = new Users(); users.find(); // Finds all users.find("John"); // Finds users by name users.find("John", "Resig"); // Finds users by first and last name users.find("John", "E", "Resig"); // Does nothing 57/128
Overloading Java vs. JavaScript – Make no mistake: JavaScript is no less “powerful” or “clean” than Java! – Different paradigms • Class + Imperative + Strongly-typed • Prototype + Functional + Dynamically-typed – Different implementations of the paradigms • Static method dispatch • Dispatch based on name and number of arguments 58/128
Polymorphism Problem: change behaviour of concrete objects Solution: polymorphism Definition (recalled) – Subtype polymorphism, almost universally called polymorphism in object-oriented programming – The ability to create a variable, a function, or an object that has more than one form – Superset of overloading, overriding 59/128
Polymorphism Definition – Subtype polymorphism is all about type-safety and the “contract” between a superclass and its subclasses – It is all about pre- and post-conditions • And directly related to co- and contra-variance 60/128
61/128
Polymorphism – Liskov’s Substitution Principle Barbara Liskov – Born 7 November 1939 Barbara Liskov *1939 – Mother of the Liskov’s substitution principle – IEEE J. von Neumann Medal in 2004 – ACM Turing Award in 2008 – Cf. http://en.wikipedia.org/wiki/ Liskov_substitution_principle 62/128
Polymorphism – Liskov’s Substitution Principle Liskov’s substitution principle – Context • 1987 – Object-oriented programming is increasingly popular – Principle • Let q(x) be a property provable about objects x of type T. Then q(y) should be true for objects y of type S where S is a subtype of T 63/128
Polymorphism – Liskov’s Substitution Principle – Principle (cont’d) • Behavioural sub-typing is different and stronger than the concept of sub-typing in type theory • In type theory – Contravariance of parameters: a parameter of type T can accept object of type S, where S is a sub-type of T – Covariance of return type: the return type can be “enlarged” from T to S • In addition – Pre-conditions cannot be stronger in a sub-type – Post-conditions cannot be weaker in a sub-type – The sub-type S must preserve the invariants of type T 64/128
Polymorphism – Liskov’s Substitution Principle Definition – If S is a subtype of T , then objects of type T may be replaced with objects of type S (objects of type S may be substitutes for objects of type T ) without altering any of the desirable properties of S (correctness, task, etc.) T P S 65/128
Polymorphism – Liskov’s Substitution Principle This principle defines the notions of generalization/specialization formally Class S is correctly defined as a specialization of T if the following are true – For each object s of class S there is an object t of class T such that the expected behavior of any program P defined in terms of T is unchanged if s is substituted for t Then – S is a said to be a subtype of T – Any instance of S can stand for an instance of T without any undesired effect on client classes – Any future extension (new subclasses) will not affect existing clients From Giuliano Antoniol’s slides INF6305 66/128
Polymorphism – Liskov’s Substitution Principle class Rectangle : public Shape { class Square : public Rectangle { private: int w, h; public: public: void set_width(int w) { virtual void set_width(int wi) { Rectangle::set_height(w); w=wi; Rectangle::set_width(w); } } virtual void set_height(int he) { void set_height(int h) { h=he; set_width(h); } } } } void foo(Rectangle *r) { // This is the client r->set_width(5); r->set_height(4); assert((r->get_width()*r->get_height()) == 20); // Oracle } • If r is instantiated at run time with an instance of square, behaviour observed by client is different ( width*height == 16 ) • Observed behaviour unexpected and may lead to problems • Square should be defined as subclass of Shape , not Rectangle From Giuliano Antoniol’s slides for INF6305. 67/128
Polymorphism – Liskov’s Substitution Principle Class S is correctly defined as a specialization of T then we say that S is compatible with T T P S 68/128
Polymorphism Class-based, typically Java class SuperClass { Object getSomething() { return new Object(); } } class SubClass extends SuperClass { String getSomething() { return new String(); } } public class CovarianceOfReturnTypes { public static void main( final String[] args) { final SuperClass superClass = new SubClass(); System. out .println( Object. class .isAssignableFrom(superClass.getSomething().getClass())); } } 69/128
Polymorphism Class-based, typically Java final SuperClass superClass = new SubClass(); final SubClass subClass = new SubClass(); 70/128
Polymorphism Class-based, typically Java class SuperClass { Object getSomething() { return new Object(); } } class SubClass extends SuperClass { String getSomething() { return new String(); } } public class CovarianceOfReturnTypes { public static void main( final String[] args) { final SuperClass superClass = new SubClass(); System. out .println( Object. class .isAssignableFrom(superClass.getSomething().getClass())); } } 71/128
Covariance of return type Objects returned by getSomething Polymorphism in any subclass are always compatible with that expected by the superclass Class-based, typically Java class SuperClass { Object getSomething() { return new Object(); } } class SubClass extends SuperClass { String getSomething() { return new String(); } } public class CovarianceOfReturnTypes { public static void main( final String[] args) { final SuperClass superClass = new SubClass(); System. out .println( Object. class .isAssignableFrom(superClass.getSomething().getClass())); } } 72/128
Polymorphism Class-based, typically Java class SuperClass { String getSomething() { return new Object(); } } class SubClass extends SuperClass { Object getSomething() { return new String(); } } public class ContravarianceOfReturnTypes { public static void main( final String[] args) { final SuperClass superClass = new SubClass(); // This whole piece of code is not legal in Java! For example only. final String result = superClass.getSomething(); } } 73/128
If contavariance of return type Objects returned by getSomething Polymorphism in a subclass would not be compatible with that expected by the superclass Class-based, typically Java class SuperClass { String getSomething() { return new Object(); } } class SubClass extends SuperClass { Object getSomething() { return new String(); } } public class ContravarianceOfReturnTypes { public static void main( final String[] args) { final SuperClass superClass = new SubClass(); // This whole piece of code is not legal in Java! For example only. final String result = superClass.getSomething(); } } 74/128
Polymorphism Class-based, typically Java class SuperClass { Object getSomething() { return new Object(); } } class SubClass extends SuperClass { String getSomething() { return new String(); } } public class CovarianceOfReturnTypes { public static void main( final String[] args) { final SuperClass superClass = new SubClass(); System. out .println( Object. class .isAssignableFrom(superClass.getSomething().getClass())); } } 75/128
Post-conditions in the subtype must Polymorphism be the same as or stronger than in the supertype Class-based, typically Java class SuperClass { Object getSomething() { return new Object(); } } class SubClass extends SuperClass { String getSomething() { return new String(); } } public class CovarianceOfReturnTypes { public static void main( final String[] args) { final SuperClass superClass = new SubClass(); System. out .println( Object. class .isAssignableFrom(superClass.getSomething().getClass())); } } 76/128
Post-conditions in the subtype must Polymorphism be the same as or stronger than in the supertype Class-based, typically Java class SuperClass { Object getSomething() { return new Object(); Post-condition } } class SubClass extends SuperClass { String getSomething() { return new String(); } } public class CovarianceOfReturnTypes { public static void main( final String[] args) { final SuperClass superClass = new SubClass(); System. out .println( Object. class .isAssignableFrom(superClass.getSomething().getClass())); } } 77/128
Post-conditions in the subtype must Polymorphism be the same as or stronger than in the supertype Class-based, typically Java class SuperClass { Object getSomething() { return new Object(); Post-condition } } class SubClass extends SuperClass { Covariance String getSomething() { return new String(); } } public class CovarianceOfReturnTypes { public static void main( final String[] args) { final SuperClass superClass = new SubClass(); System. out .println( Object. class .isAssignableFrom(superClass.getSomething().getClass())); } } 78/128
Polymorphism Class-based, typically Java class SuperClass { void getSomething( final String aParameter) { System. out .println("One implementation..."); } } class SubClass extends SuperClass { void getSomething( final Object aParameter) { System. out .println("Another implementation..."); } } public class ContravarianceOfArgumentTypes { public static void main( final String[] args) { final SubClass subClass = new SubClass(); subClass.getSomething( new Object()); subClass.getSomething(""); } } 79/128
Contravariance of arguments types Objects used by getSomething in Polymorphism any subclass are always narrower to allow calling the super method Class-based, typically Java class SuperClass { void getSomething( final String aParameter) { System. out .println("One implementation..."); } } class SubClass extends SuperClass { void getSomething( final Object aParameter) { System. out .println("Another implementation..."); } } public class ContravarianceOfArgumentTypes { public static void main( final String[] args) { final SubClass subClass = new SubClass(); subClass.getSomething( new Object()); subClass.getSomething(""); } } 80/128
Polymorphism Class-based, typically Java class SuperClass { void getSomething( final Object aParameter) { System. out .println("One implementation..."); } } class SubClass extends SuperClass { void getSomething( final String aParameter) { System. out .println("Another implementation..."); } } public class ContravarianceOfArgumentTypes { public static void main( final String[] args) { final SubClass subClass = new SubClass(); // This piece of code is not legal in Java! For example only. subClass.getSomething(""); } } 81/128
Covariance of arguments types Objects used by getSomething in Polymorphism any subclass would confuse the compiler: which method to use? Class-based, typically Java class SuperClass { void getSomething( final Object aParameter) { System. out .println("One implementation..."); } } class SubClass extends SuperClass { void getSomething( final String aParameter) { System. out .println("Another implementation..."); } } public class ContravarianceOfArgumentTypes { public static void main( final String[] args) { final SubClass subClass = new SubClass(); // This piece of code is not legal in Java! For example only. subClass.getSomething(""); } } 82/128
Polymorphism Class-based, typically Java class SuperClass { void getSomething( final String aParameter) { System. out .println("One implementation..."); } } class SubClass extends SuperClass { void getSomething( final Object aParameter) { System. out .println("Another implementation..."); } } public class ContravarianceOfArgumentTypes { public static void main( final String[] args) { final SubClass subClass = new SubClass(); subClass.getSomething( new Object()); subClass.getSomething(""); } } 83/128
Pre-conditions in the subtype must be Polymorphism the same as or weaker than in the supertype Class-based, typically Java class SuperClass { void getSomething( final String aParameter) { System. out .println("One implementation..."); } } class SubClass extends SuperClass { void getSomething( final Object aParameter) { System. out .println("Another implementation..."); } } public class ContravarianceOfArgumentTypes { public static void main( final String[] args) { final SubClass subClass = new SubClass(); subClass.getSomething( new Object()); subClass.getSomething(""); } } 84/128
Pre-conditions in the subtype must be Polymorphism the same as or weaker than in the supertype Class-based, typically Java class SuperClass { void getSomething( final String aParameter) { System. out .println("One implementation..."); Pre-condition } } class SubClass extends SuperClass { void getSomething( final Object aParameter) { System. out .println("Another implementation..."); } } public class ContravarianceOfArgumentTypes { public static void main( final String[] args) { final SubClass subClass = new SubClass(); subClass.getSomething( new Object()); subClass.getSomething(""); } } 85/128
Pre-conditions in the subtype must be Polymorphism the same as or weaker than in the supertype Class-based, typically Java class SuperClass { void getSomething( final String aParameter) { System. out .println("One implementation..."); Pre-condition } } class SubClass extends SuperClass { Contravariance void getSomething( final Object aParameter) { System. out .println("Another implementation..."); } } public class ContravarianceOfArgumentTypes { public static void main( final String[] args) { final SubClass subClass = new SubClass(); subClass.getSomething( new Object()); subClass.getSomething(""); } } 86/128
Polymorphism Contra-variance in more depth… Thank you http://www.jdoodle.com , Firefox Abduction!, and Eclipse compilers 87/128
88/128
with v1.3 89/128
90/128
91/128
92/128 https://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29
Not Another implementation… Another implementation… 93/128 https://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29
94/128
Valid! Valid! 95/128
96/128
Workaround! 97/128
98/128 https://stackoverflow.com/questions/2995926/why-is-there-no-parameter-contra-variance-for-overriding
99/128 Thank you to Willian Flageol for the example
100/128 https://underscore.io/blog/posts/2015/11/04/late-binding-and-overloading.html
Recommend
More recommend