CSE116 / CSE504 Introduction to Computer Science II Dr. Carl Alphonce 343 Davis Hall alphonce@buffalo.edu Office hours: Thursday 12:00 PM – 2:00 PM Friday 8:30 AM – 10:30 AM OR request appointment via e-mail PROFESSIONALISM Turn off and put away electronics: cell phones pagers laptops tablets etc. © Dr. Carl Alphonce ROADMAP Where we’ve been LRStruct visitors Stack implementations Today Encapsulation discussion Queue implementations channel 1 This visitor, when executed on an LRStruct<E>, returns true if arg is found in the LRStruct, false otherwise. What must you write in the red box below to complete the definition of the visitor? public class SearchVisitor<E> implements IAlgo<Boolean,E,E> { @Override public Boolean emptyCase(LRStruct<E> host, E arg) { return false; } @Override public Boolean nonEmptyCase(LRStruct<E> host, E arg) { return host.getDatum().equals(arg) || host.getRest().execute(this,arg); } } Rank 1 2 3 Responses OR + && 4 5 6 AND == OTHER Keyword: ||, OR Keyword Matches: 95 This visitor, when executed on an LRStruct<E>, returns true if arg is found in the LRStruct, false otherwise. What must you write in the red box below to complete the definition of the visitor? public class SearchVisitor<E> implements IAlgo<Boolean,E,E> { @Override public Boolean emptyCase(LRStruct<E> host, E arg) { return false; } @Override public Boolean nonEmptyCase(LRStruct<E> host, E arg) { return host.getDatum().equals(arg) || host.getRest().execute(this,arg); } } Rank 1 2 3 4 5 6 Responses Other This visitor, when executed on an LRStruct<E>, returns true if arg is found in the LRStruct, false otherwise. What must you write in the red box below to complete the definition of the visitor? public class SearchVisitor<E> implements IAlgo<Boolean,E,E> { @Override public Boolean emptyCase(LRStruct<E> host, E arg) { return false; } @Override public Boolean nonEmptyCase(LRStruct<E> host, E arg) { return host.getDatum().equals(arg) || host.getRest().execute(this,arg); } } Rank 1 2 3 Responses OR && 1 4 5 6 ? TRUE : ANDAND OTHER Keyword: ||, OR Keyword Matches: 139 Stack<E> A stack is a Last-In First-Out (LIFO) data structure. It must enforce the LIFO access policy, permitting direct access only to the top element of the stack. The allowable operations are: void push(E item) – adds item at the top of the stack E pop() – removes (and returns) the top element from a non-empty stack E peek() – returns (but does not remove) the top element from a non-empty stack We don’t want to define our stack from first principles (like we did with the Bag). Instead, we want to define Stack in terms of some existing class. Stack<E> When we set out to do this (define a new class A in terms of an existing class B), we generally have two options: 1) define A by composition with B public class A { private B _b; public A() { _b = new B(); } } 2) define A by inheritance from B public class A extends B { ... } java.util.Stack<E> java.util.Stack<E> is defined by inheritance with Vector<E>. Vector<E> is similar in functionality to the ArrayList<E>. Because java.util.Stack<E> extends Vector<E>, all the methods of Vector<E> are callable on a java.util.Stack<E> object. This means that it is a stack in name only – the class cannot enforce the LIFO access policy that should define a stack. This class is a well-known example of making the wrong implementation choice. Composition can easily be used to define a new class A which has more restricted functionality than the composed class B. Inheritance is used to define a class A which has additional functionality beyond what its parent class B has. Exercise We split the class in half; one half defined Stack<E> by composition with ArrayList<E>, the other half defined Stack<E> by composition with LRStruct<E>, from these starting points: public class Stack<E> { private ArrayList<E> _list; public Stack() { _list = new ArrayList<E>(); } public void push(E item) { ... } public E pop() { ... } public E peek() { ... } } public class Stack<E> { private LRStruct<E> _list; public Stack() { _list = new LRStruct<E>(); } public void push(E item) { ... } public E pop() { ... } public E peek() { ... } } public class Stack<E> { private ArrayList<E> _list; public Stack() { _list = new ArrayList<E>(); } public void push(E item) { _list.add(_list.size(), item); } public E pop() { return _list.remove(_list.size()-1); } public E peek() { return _list.get(_list.size()-1); } public boolean isEmpty() { return _list.isEmpty(); } } public class Stack<E> { private LRStruct<E> _list; public Stack() { _list = new LRStruct<E>(); } public void push(E item) { _list.insertFront(item); } public E pop() { return _list.removeFront(); } public E peek() { return _list.getDatum(); } public boolean isEmpty() { return _list.isEmpty(); } return _front.execute(new EmptyVisitor<E>(), null); } public class EmptyVisitor<E> implements IAlgo<Boolean,E,Void> { @Override public Boolean emptyCase(LRStruct<E> host, Void _) { return true; } @Override public Boolean nonEmptyCase(LRStruct<E> host, Void _) { return false; } } Exercise - observations Stack<E> by composition with ArrayList<E> The top of the stack is mapped to *back* of the ArrayList. This is more efficient than mapping it to the *front*, as doing the latter would require copying data on each push operation, and also on each pop operation. If the stack is empty the pop and peek methods throw an IndexOutOfBoundsException. Stack<E> by composition with LRStruct<E> The top of the stack is mapped to the *front* of the LRStruct. If the stack is empty the pop and peek methods throw an IllegalStateException. Exercise - encapsulation A client of the Stack<E> class will become dependent on the particular implementation: Stack<String> st = new Stack<String>(); try { st.peek(); } catch ( ... what kind of exception is thrown? ... ) The exception thrown is determined by the internal implementation details. This is bad – because changes in those internal implementation details can affect client code!