CS 541 Meeting -*- Outline -*- * Types and Classes in OOP (and Java) idea: (static) analysis to verify certain program properties and prevent run-time type errors ** Basic concepts *** type and type error ------------------------------------------ BASIC CONCEPTS OF TYPE AND CLASS def: the protocol (interface) of an object is def: a type is Some questions: - Who decides what constitutes a type? - What is a type error? ------------------------------------------ ... the set of messages it can respond to (without error, sensibly) Q: What should a type be? ... a protocol (or perhaps the name of a protocol) or perhaps better: a set of objects that obey a protocol. ... the language? the programmer? if it's the programmer, then we can avoid confusing objects with the same protocol but different semantics so it would be an ADT this is essentially the "by name" vs "structural" type checking idea the same distinction applies to subtyping (i.e., does it correspond to behavioral subtyping?) Java is by name for typing, as we'll see ... assignment of value of type T to a variable of type S? so what? treating a T as a S? why do we care? to prevent type errors -- but that's circular to prevent message not understood errors -- ok Donahue: all objects are bit strings, want to make sure don't misinterpret them What's a misinterpretation? perhaps interpretation outside an implementation module *** behavioral reasoning aided by type checker Q: can we make even stronger behavioral guarantees? ------------------------------------------ REASONING ABOUT ADTs Data type induction argument example: Sets represented by lists To prove: every Set's list has no duplicates How to prove this? ------------------------------------------ show it holds when sets are created, and that if it holds for a set argument (e.g., self) then it holds for any mutated or created sets Q: What part of the program has to be examined for this to work? *** Seals (Morris 1973) way to describe invariant preservation want module to be able to seal data before passing it to world ------------------------------------------ SEAL GOALS Only by using the ADTs ops can a client: alter = discover = Clients cannot: impersonate = SEALS MECHANISM createSeal -> (seal_t, unseal_t) gives unique seal and unseal functions Properties: unseal_t(seal_t(x)) = x unseal_t(anything else) is an error ------------------------------------------ ... change state of an object ... access components of object ... fake an object of the ADT Q: How could these cause problems for a program that kept a table in sorted order? Q: Does Smalltalk prevent impersonation? no, no type concept idea: limit use of seal_t to a small area of code Q: How does this allow for data type induction? want to prove for all x: P(unseal_t(x)) must show that for all y: if seal_t is applied to y, then P(y) holds. Q: How could we use seals to define what a "type error" means? attempt to access sealed data without proper capability each object sealed according to its type (seal_T). set of primitive operations have access to unseal_T Q: How does this relate to security in Java? *** static and conservative nature of type systems ------------------------------------------ TYPE SYSTEMS AS STATIC APPROXIMATIONS Does this code have a type error? int i = 0; Foo f; bool test = myReader.read_bool(); if (test) { i = i + 1; } else { f = i; } ------------------------------------------ Yes, because we might execute the else part since can't know for sure, assume the worse this is a conservative approximation Q: Why are all elements of an array assumed to have the same type? so don't have to know the run-time state Hence generally have typed containers (variables) - typed containers: container of type T may only point to T objects allows earlier catching of errors seals unnecessary for objects in container e.g., savings for arrays - typed objects only: containers untyped can only catch errors on access to contained object seals must be stored with object ------------------------------------------ BASIC STRATEGY OF STATIC TYPE SYSTEMS Variables have a statically declared type Expresssions are assigned a type based on Examples: ------------------------------------------ ... the types of their subexpressions, and the signatures of operators (typing rules), inductively ... int i; (i + 3 - 2) * 4 *** soundness and completeness ------------------------------------------ SOUNDNESS AND COMPLETENESS A type system is *sound* iff A type system is *complete* iff ------------------------------------------ ... whenever the type system says an expression has a type, and the expression can be evaluated to a value, then the evaluation does not produce a type error. ... whenever the evaluation does not produce a type error, the type system sas it has a type. Don't usually get completeness, since it's conservative. Q: How to prove this? prove something stronger: that if the type system says an expression E has type T, then the value of E has type T. This property is sometimes taken as the definition of "strong typing". ** subtyping *** the motivation: polymorphism ------------------------------------------ POLYMORPHISM def: code is *polymorphic* if e.g., displayAll: objList objList do: [ :o | o display ] ------------------------------------------ ... it works (in approximately the same way) for 2 or more classes (we could always say it works for just one type, the one with the required protocol, so "classes" is really needed) because of message passing, all code in Smalltalk is polymorphic; it works for any type of object that obeys a certain protocol. (in C++ you have to choose to get message passing and hence don't always have this flexibility) e.g., a window may contain a list of objects to display, its refresh method sends the "displayOn:" message to each, doesn't have to do case TypeOf(o) rectangle: rectangle$display(o) circle: circle$display(o) etc. so it's polymorphic works even for stuff you haven't thought of yet! when add new type of data object don't have to go through the code looking for the case stmts Q: What's the language design tradeoff here? *** protocols, types, and behavior ------------------------------------------ PROTOCOLS, TYPES, and BEHAVIOR def: the behavioral protocol of an object is def: S is a subtype of T if def: S is a behavioral subtype of T if ------------------------------------------ ... the set of messages it can respond to (without error, sensibly) ... its protocol together a specification of the effect (meaning) of each message ... the protocol of T is part of the protocol of S, i.e., if the interface of T contains the interface of S Q: What would it mean for S to not be a subtype of T? Q: What should a behavioral subtype be? ... it is a subtype and also that its objects obey the behavioral protocol of T Q: What would it mean for S to not be a behavioral subtype of T, and still be a subtype of T? *** subtype vs. inheritance (see Gamma et al., pages 16-17) Q: What does subtyping have to do with inheritance? Q: What do we know about these? ------------------------------------------ SUBTYPING VS. INHERITANCE | ===================|====================== class | | subclass | | type | | subtype | | behavioral subtype | | ------------------------------------------ can make analogy that a subtype inherits protocol and a behavioral subtype inherits behavior (or specifications of it) e.g., Set and Interval want Interval (immutable) to be a subtype of Set, but don't want to use the same representation. (in Smalltalk/V, Interval does not inherit from Set) ------------------------------------------ ORTHOGANALITY EXAMPLE IntSetType ^ IntSetClass | subtype | IntervalType IntervalClass ------------------------------------------ Q: How does C++ handle this? Java? Q: What if we required subclass = subtype? We'll see that when we study binary methods (later) that a subclass doesn't necessarily generate a subtype. Q: What can we do to make a subclass not be a behavioral subtype? override methods (e.g., to loop forever or give errors) we'll come back to this issue when we discuss abstract classes ** types in Java Reference: Gosling, Joy, Steele: The Java Language Specification *** security and types in the JVM Soundness matters in Java, see the book "Java Security", by McGraw and Felten ------------------------------------------ WHY TYPE SOUNDNESS MATTERS IN JAVA OR HOW THEORETICIAL WORK CAN MAKE YOU FAMOUS class T { SecurityManager x; ... } class U { MyObject x; ... } T t; U u; // ...somehow make u and t aliases... t.x = System.getSecurity(); MyObject m = u.x; ------------------------------------------ the miracle is how to do the comment, if it can be done, then can make m a pointer to the system's Security Mmanager object, with type MyObject. Then all hell breaks loose *** overview (skip) ------------------------------------------ TYPES IN JAVA Types: primitive types: byte, short, int, long float, double char boolean reference types: classes, interfaces, T[] ------------------------------------------ there is one value without a type: null but it is considered a value of every reference type **** primitives exact range of values is specified for each primitive type no machine dependencies IEEE 754 standard floating point unicode characters for internationalization for each primitive there is also a parallel class, which allows the primitive values to be put in class instances. Why? **** references recall that a reference is null or a pointer to an object interfaces are object types (protocols only) array is essentially the only template class in Java; each instance, such as int[], inherits from Object. *** interfaces, classes and types (skip) ------------------------------------------ CLASSES AND TYPES Every class name Object is the superclass of all classes and of all array classes SUBCLASS ==> SUBTYPE class T {...} class S extends T {...} T x = new S(); CHECKED CASTING casting to a reference type generates ------------------------------------------ ... is a type name ... an exception if the object cast is neither null nor a subclass of that reference type (or does not have a class that implements the type) Note: no protected, private inheritance as in C++ classes can be declared abstract final classes can't be extended single inheritance only ------------------------------------------ INTERFACES // example public interface Colorable { byte CLEAR = 0; void setColor(byte r, byte b, byte g); } Contains declarations of: constants, and abstract methods ------------------------------------------ members are implicitly public, methods are implicitly abstract no constructors or static methods allowed So these are pure object types, i.e. protocols ------------------------------------------ INTERFACE EXTENSION public interface PointType { int x(); int y(); } public interface ColorPointType extends Colorable, PointType { byte redness(); byte greenness(); byte blueness(); } ------------------------------------------ Note that multiple superinterfaces (supertypes) are allowed for interfaces ------------------------------------------ INTERFACE IMPLEMENTATION class ColorPoint extends Point implements ColorPointType { byte r, g, v; public ColorPoint(int xv, int yv, byte rv, byte gv, byte bv) { super(xv, yv); r = rv; g = gv; b = bv; } public byte redness() {...} public byte greenness() {...} public byte blueness() {...} ... } class PolarColorPoint implements ColorPointType { ... } ------------------------------------------ Q: Why doesn't Java infer when a class implements a type? so that types are behavioral ------------------------------------------ EXTENSION AND IMPLEMENTATION SUMMARY Point PointType Colorable | \ / | \ / | \ / ColorPoint -------- ColorPointType / / PolarColorPoint ------------------------------------------ put in the extends, and implements labels add: a class PolarPoint that extends Point, show that it implements PointType an implements link from Point implements PointType, and point out that ColorPoint would also implement PointType ------------------------------------------ USE OF INTERFACE TYPES class Demo { public static void main(String[] args){ ColorPointType myCP; if (args.length > 1) { myCP = new ColorPoint(3,4,8,9,2); } else { myCP = new PolarColorPoint(...); } ... myCP.redness() ... } } ------------------------------------------ Q: Can Java still do type checking when interface types are used? ** inheritance and requirements on types of methods ------------------------------------------ INHERITANCE AND METHOD TYPES class Object {...} class Number extends Object {...} class Integer extends Number {...} class Point { Number x() {...} void set_x(Number xv) {...} ... } class ColorPoint extends Point { x() { ... } void set_x( xv) ... } Point myP = new ...; myP.x() myP.set_x( ------------------------------------------ Q: What can the return type of the ColorPoint's x method be? Number or Integer, but not Object (covariant) Q: What can the argument typ of ColorPoint's set_x method be? Number or Object, but not Integer (contravariant) Q: Why? So that the same calls are legal, and still check Java actually generates an overload if you change an argument type. What's the advantages or disadvantages of that? Q: What about exceptions? Can you allow more or less in the subclass? ------------------------------------------ STATIC OVERLOADING vs. DYNAMIC OVERRIDES (MESSAGE PASSING) class Foo { public int f (Float f) { return 1; } } class Bar extends Foo { public int f (String s) { return 2; } } // client code Foo x = new ...; x.f (new Float(3.14)); x.f ("a string"); // static type error ------------------------------------------ the given methods are statically overloaded Explain how to subscript method names with their types to explain what happens with static overloading. One can also do the following, to make the types work try ((Bar) x).f("a string"); catch (ClassCastException e) {...} Add a dynamically overridden method // Foo's int g (int i) { return 3; } // Bar's int g (int i) { return 4; } // client code: x.g(5) Now consider what happens if you add a new method to each, making g both statically overloaded, and some of the overloads dynamically dispatched. // Foo's int g (int i, Point p) { return 6; } // Bar's int g (int i, ColorPoint cp) { return 7; } int g (int i, Point p) { return 8; } ** arrays and templates ------------------------------------------ ARRAYS IN JAVA Suppose ColorPoint is a subtype of Point. Is ColorPoint[] a subtype of Point[]? ------------------------------------------ Look at the types of the fetch and store operations The fetch operation __[__] : Point[], int -> Point __[__] : ColorPoint[], int -> ColorPoint which is compatible with ColorPoint[] a subtype of Point[] The store operation __[__] = __ : Point[], int, Point -> void __[__] = __ : ColorPoint[], int, ColorPoint -> void which is *not* compatible with ColorPoint[] a subtype of Point[] But in Java, alas, ColorPoint[] a subtype of Point[]! So why isn't the type system unsound? Because there's a run-time check on the store operation! The actual store operation has the type __[__] = __ : Point[], int, Object -> void throws ArrayStoreException __[__] = __ : ColorPoint[], int, Object -> void throws ArrayStoreException if you try to store a Point into a ColorPoint[] array, then this exception happens at run-time This is clearly a mistake in the design, as type checking no longer statically guarantees against such simple type errors. So why was it done? ------------------------------------------ TEMPLATES What would the subtyping rule be for templates (if Java had them)? Example: Is X a subtype of X if S is a subtype of T? ------------------------------------------ No, suppose X is Vector, or Array! Q: How would you declare the relationships that might exist? it's complex