CS 3500 Fall 2013 Lecture 3: Review of Liskov - Chapters 1-3 ------------------------------------------ Topics: ------- Review of readings in Liskov Chapter 1: Abstractions Review of readings in Liskov Chapter 2: Understanding Objects in Java Liskov Chapter 3: Procedural Abstraction Liskov Chapter 3: Exceptions Abstractions: ------------- Abstraction by parametrization: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ a simple concept, we define unctions and methods that consume arguments specified by the parameter list. This make the function/method usable for any selection of argument values. Abstraction by specification: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Still designing functions/methods, but adding to them requirements: double sqrt(double coef) { // REQUIRES: coef > 0 // EFFECTS: Returns an approximation to the square root of clef double ans = coef / 2.0; int i - 1; while (i < 7) { and = ans - ((ans * ans - coef) / ( 2.0 * ans)); i = i + 1; } return and; } Requires assertion - also called the 'precondition' Effects assertion - also called the 'postcondition This is a contract between the client and the implementor Data abstraction: ~~~~~~~~~~~~~~~~~ is a set of operations to specify the behavior of a data type Our examples will include Set, MultiSet, FSet (immutable set), and others. In the past we have seen Stack, Queue, even BinarySearchTree. Iteration abstraction: ~~~~~~~~~~~~~~~~~~~~~~ the iterator generates the elements of some data set in some order (we have no control over), but produces all of the elements, and we can tell when we have seen all. The procedures that use the generated data do not care how the data set that is the source of data is organized - all it needs is the access to the data elements one at a time. Type families: ~~~~~~~~~~~~~~ sub-types and super-types enable us to extend the data definition or to make several data types implement the same common behavior specified by the super type Note: the book does not mention the parametrized types, as at the time the book was written, Java did not have them. Understanding Java Objects: --------------------------- This is a review of mostly what we already know or have covered in the first two lectures. We just highlight the important points. Mutable objects vs. immutable -- String is immutable - and it has been a source of headaches - so Java added StringBuffer - which is mutable -- primitive types are immutable -- we may want some classes to contain only immutable objects - we need to design programs that enforce it -- our first assignment asks you to design a representation of an immutable Set Shared objects object is shared if there are several references to it. I may define a Person pete, then add pete to some ArrayList a list at index 3, now I can refer to pete in two ways - and even if I no longer can access 'pete', I can get the same object by asking for a list.get(3). Method call - the dispatch - we already understand this - the runtime type determines where we start looking for the method to invoke… Type checking static - i.e. done at the compile time - whenever possible we can use 'casting' to inform the compiler about the type to use, and take the blame if it fails at runtime (ClassCastException) Java includes automatic checks for Array bounds at run time. Type conversions -- done automatically between various types of numeric values, and between the primitive types and the wrapper classes. Sestoft is the best reference for this - we will say no more. Method overloading -- no big deal - there can be several methods with the same name - they are distinguished by the number and the types of their parameter list. Vector vs. ArrayList Vector is thread-safe, ArrayList is not. Otherwise they behave the same. thread-safe requires extensive runtime checks and is costly so we compromise -- more about this later when we talk about iterators as well. Stream I/O We will talk about it later. For now - reading data from a stream delivers one character at a time, trying to make sense out of it is a tedious process, especially when dealing with formatted data - numbers, etc. System.in - system inpput System.out - system output System.err - system error output stream FileReader -- connects to an eternal file and delivers one character at a time Java application public static void main(String[] argv) - we talked about it Procedural Abstraction: ----------------------- reiterate: - abstraction by parametrization (procedure consumes arguments specified by the procedure parameters) - abstraction by specification (the procedure specification describes the preconditions (requires) and postconditions (produces, modifies, effects) Properties: -- locality -- you understand what the procedure does without knowing how and can use it freely -- modifiability -- the implementor can substitute a different implementation without any impact on the client code examples from pages 45-47 Properties of procedures and their implementations: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- minimality -- one specification is more minimal than other if it contains fewer constraints -- undetermined behavior -- for some inputs it may produce more than one possible outcome (e.g. sort - what do you do with duplicates?) -- deterministic implementations -- for the same inputs always the same outputs (not a random move of a game object) -- generality -- one specification is more general that another if it can handle a more general class of inputs examples: sort(ArrayList) sort(ArrayList) sort(Iterable) Total vs partial procedures: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - total procedure: -- works for all legal values of inputs - partial procedure: -- needs the 'REQUIRES' clause to specify the constraints on the inputs (for example sortedSearch requires a sorted Array) Comments: -- partial procedures are less safe than total -- when possible that partial procedure should verify that the REQUIRES clause is satisfied (but this is not always possible or feasible) (makes no sense to check that an Array is sorted) Exceptions: ----------- Use Exceptions to signal any breach of contract (when feasible) makes the implementation more robust Examples: public static int factorial (int n) throws NonPositiveException // EFFECTS: if n is non-positive throws NonPositiveException // returns the factorial of n public static int search (Vector v, int x) throws NullPointerException, NotFoundException // REQUIRES: v is sorted // EFFECTS: if v is null throws NullPointerException // if x is not found in v throws NotFoundException // else returns i such that v.get(i) = x (a question to ponder - is this a deterministic specification?) Types of exceptions: Exception any subclass of Exception requires that it be checked i.e., must be declared in the procedure header with the 'throws' clause and must be caught in the client code with try-catch clause subclass: RuntimeException and any subclasses of RuntimeException do not have to be checked that means, the procedure does not have to include the 'throws' clause and the client does not need to use the try-catch clause (to be continued…)