You are on page 1of 11

05/05 JAVA LECTURE interface StringSet { boolean isEmpty(); void add(String s); boolean contains(String s); String get();

; } /* StringSet s = new ArraySet(); if(s.isEmpty()) { s.add("hello"); } */ class ArraySet implements StringSet { protected String[] elems; protected int size; public boolean isEmpty() { return size == 0; } public boolean equals(Object o) { ... } } // generics, or parametric polymorphism interface Set<E> { boolean isEmpty(); void add(E item); boolean contains(E item); E get(); } /* Set<String> s1 = ...; s1.add("hello"); Set<Shape> s2 = ...; s2.add(new Circle()); */ /* subtype polymorphism */ interface RemovableStringSet extends StringSet { void remove(String s); } class RemovableArraySet extends ArraySet implements RemovableStringSet { // inheriting all the operations from ArraySet public void remove(String s) { ... } } /* subtype polymorphism: if T1 is a subtype of T2, then values of type T1 can be used wherever values of type T2 are expected */ void union(StringSet s1, StringSet s2) { ... } RemovableStringSet rs = ...; union(rs, new ArraySet());

RemovableStringSet rs = ...; StringSet s = rs; rs.add("hello"); rs.remove("hello"); s.remove("hello"); // compile-time type error interface ObjectSet { boolean isEmpty(); void add(Object s); boolean contains(Object s); Object get(); } ObjectSet s = ...; s.add("hello"); // typechecks because String is a subtype of Object Object o = s.get(); // ack! lost the fact that this is a set of strings Object is the top of the class hierarchy, implicitly class Object { String toString(); boolean equals(Object equals); } Don't confuse subtyping with inheritance: Subtyping is an "interface-level" property about behavioral/semantic compatibility: - S is a subtype of T if I can use an S wherever a T is expected Inheritance is an "implementation-level" property that allows for code reuse - if C inherits from D, then C is reusing some of the methods and fields of D - just a convenience: I could just copy the methods/fields of D into C manually * Subtyping without inheritance: You might want to consider a Square to be a subtype of Rectangle. - allows squares to be passed wherever Rectangles are expected interface Rect { ... } class Rectangle implements Rect { int length; int width; ... } class Square implements Rect { int side; ... } * Inheritance without subtyping - want Bags and Sets to share code without being in a subtype relationship - wouldn't make sense since they do different things for add() (one allows duplicates; the other does not) abstract class Collection { Object[] elems; ... // shared methods } class Bag extends Collection { ... }

class Set extends Collection { ... } Dynamic dispatch: on a method call, invoke the "most-specific" method implementation for the run-time class of the receiver object class C { void n() { this.m(); } void m(); } class D extends C { void m() {} } D d = new D(); C c = d; d.m(); // invokes D's m method c.m(); // invokes D's m method d.n(); c.n(); // invokes D's m method // invokes D's m method

void myMethod(C c) { c.n(); } class MyClass { private List lst = new ArrayList(); } Good OO style: an object knows how to do certain things. - client should manipulate an object only through its interface - client never knows what implementation the object uses Example: chess game interface Piece { ... } class Rook implements Piece { ... } class Pawn implements Piece { ... } ... class Board { void badMove(Piece p, int x, int y) { if (p instanceof Pawn) { ... check whether it's a legal move, and if so make it } else if (p instanceof Rook) { ... } } void goodMove(Piece p, int x, int y) { if (p.isValidMove(x,y)) { p.move(x,y); } } }

05/12 EXCEPTIONS PAIR.JAVA

interface Pair<F,S> { F first(); S second(); } class IntStringPair implements Pair<Integer,String> { public Integer first() { return 0; } public String second() { return "hi"; } } interface Drawable { void draw(); } class Shape implements Drawable { } public void draw() { }

class PairImpl<T1 extends Drawable, T2 extends Drawable> implements Pair<T1,T2>, Drawable { protected T1 fst; protected T2 snd; PairImpl(T1 f, T2 s) { fst = f; snd = s; } public String toString() { return "(" + fst.toString() + ", " + snd.toString() + ")"; } public void draw() { fst.draw(); snd.draw(); } public T1 first() { return fst; } public T2 second() { return snd; }

class Main { public static void main(String[] args) { Pair<Integer,String> p = new IntStringPair(); // // Pair<Integer,String> p2 = new PairImpl<Integer,String>(new Integer(45), "hello"); Pair<Shape,Shape> p2 = new PairImpl<Shape,Shape>(new Shape(), new Shape()); System.out.println(p2.toString()); Integer i = p.first(); int j = i.intValue() + 45; System.out.println("first is " + i.toString()); } }

EXCEPTIONS.JAVA

class OutOfBoundsException extends Exception { OutOfBoundsException() {} OutOfBoundsException(String message) { super(message); } } // unchecked exception class FatalError extends RuntimeException {} class OtherException extends Exception {} interface List<T> { void add(int i); T get(int i) throws OutOfBoundsException; } class ListImpl<T> implements List<T> { // add an element at position i public void add(int i) { } void helper() throws OtherException { throw new OtherException(); } // get the element at position i public T get(int i) throws OutOfBoundsException { if (i < 0) throw new OutOfBoundsException("negative index"); // ... if (i > 1000) throw new FatalError(); try { this.helper(); } catch(OtherException e) { System.out.println("caught the exception"); } // can have multiple catch blocks // catch(AnotherException e) { // ... } return null; }

// //

05/19 VISITOR.JAVA // visitor pattern: adding new operations to existing types // represent a new operation on Nodes that returns a String interface Visitor { String visitEmptyNode(EmptyNode n); String visitValueNode(ValueNode n); } class ConcatAll implements Visitor { public String visitEmptyNode(EmptyNode n) { return ""; } public String visitValueNode(ValueNode n) { return n.value + n.next.accept(this); }

/* ListNode n = ...; n.accept(new ConcatAll()); */ interface Node { int size(); String accept(Visitor v); /* generic version: <A,R> R accept(Visitor v, A a); */

class EmptyNode implements Node { public int size() { return 0; } public String accept(Visitor v) { return v.visitEmptyNode(this); }

class ValueNode implements Node { String value; Node next; ValueNode(String v, Node n) { value = v; next = n; } public int size() { return 1 + next.size(); } public String accept(Visitor v) { return v.visitValueNode(this); } }

05/17 NODES.JAVA import java.util.Iterator; import java.lang.Iterable; // an exception for places in the code that you need to implement class ImplementMe extends RuntimeException {} // an interface for sets of strings interface Set { // is string s in the set? boolean contains(String s); // add an element; no duplicates allowed void add(String s); // how many elements are in the set? int size(); } // a type for nodes in the linked list interface ListNode { boolean contains(String s); ListNode add(String s); int size(); } // a sentinel indicating that we've reached the end of the linked list class EmptyNode implements ListNode { ListSet set; EmptyNode(ListSet s) { this.set = s; } public boolean contains(String s) { return false; } public ListNode add(String s) { return set.valuenode(s, this); } public int size() { return 0; } public String value() { return null; } public ListNode next() { return null; } } class ValueNode implements ListNode { // the element stored in this node protected String value; // a pointer to the next node in the list protected ListNode next; ValueNode(String value, ListNode next) { this.value = value; this.next = next; } public boolean contains(String s) { return s.equals(value) || next.contains(s); } public ListNode add(String s) { next = next.add(s);

return this;

public int size() { return 1 + next.size(); } public String value() { return value; } public ListNode next() { return next; }

class ListSet implements Set { // the head of the linked list of nodes protected ListNode head; ListSet() { head = this.emptynode(); } // factory methods public ListNode emptynode() { return new EmptyNode(this); } public ListNode valuenode(String s, ListNode n) { return new ValueNode(s, n); } public boolean contains(String s) { return head.contains(s); } public void add(String s) { if(contains(s)) return; head = head.add(s); } public int size() { return head.size(); }

class SortedValueNode extends ValueNode { SortedValueNode(String s, ListNode n) { super(s,n); } } class SortedListSet extends ListSet { // no need to create a new kind of empty node now! public ListNode valuenode(String s, ListNode n) { return new SortedValueNode(s, n); } SortedListSet() { super(); } } class Test { public static void main(String[] args) { Set s1 = new ListSet(); s1.add("hello"); s1.add("hi"); s1.add("hello"); System.out.println("test 1: " + (s1.contains("hi") == true)); System.out.println("test 2: " + (s1.contains("bye") == false)); System.out.println("test 3: " + (s1.size() == 2));

05/19 MULTIMETHOD3.JAVA /* double dispatch: - gets the right behavior - cumbersome, error-prone (but at least no casts) - tedious to extend */ interface Shape { boolean intersect(Shape s); boolean intersectRect(Rectangle r); boolean intersectTri(Triangle t); } class Rectangle implements Shape { public boolean intersect(Shape s) { return s.intersectRect(this); } public boolean intersectRect(Rectangle r) { /* implement intersection of a rect and a rect */ System.out.println("rect, rect"); return true; } public boolean intersectTri(Triangle t) { /* implement intersection of a rect and a tri */ System.out.println("rect, tri"); return true; } } class Triangle implements Shape { public boolean intersect(Shape s) { return s.intersectTri(this); } public boolean intersectRect(Rectangle r) { /* implement intersection of a rect and a tri */ System.out.println("tri, rect"); return true; } public boolean intersectTri(Triangle t) { /* implement intersection of a tri with a tri */ System.out.println("tri, tri"); return true; } } class Client { public static void main(String[] args) { Shape r = new Rectangle(); Shape t = new Triangle(); r.intersect(t); }

05/31 MGU
*unification* E1 = E2 tries to find a substitution s (mapping from variables to terms) such that s(E1) = s(E2) (the trees are equal) otherwise says "no" ---s is a *unifier* for t1 and t2 if s(t1) = s(t2). there's always a *most general unifier* (MGU) or substitution for any two terms that unify: s is an MGU for t1 and t2 if s is a unifier for t1 and t2 and for any other unifier s', there exists a substitution s'' such that s''(s(t1)) = s'(t1) and s''(s(t2)) = s'(t2) example: f(X,Y) = f(a,Y)

{X->a, Y->a} {X->a, Y->b} ... MGU: {X->a} t=term, c=constant, a=atom, X=variable

Let's define MGU: MGU(c,c) = {} MGU(X,X) = {} mgu(x,t) = {x->t}

if x not in t, else "no"

[occurs check]

mgu(t,x) = {x->t} if x not in t, else "no" mgu(a(t1,...,tn), a(t1',...,tn')) = for each i, mgu(ti, ti') = si if any of those fails to unify, then return "no" otherwise if s1 u ... u sn is inconsistent return "no" else return s1 u ... u sn mgu(_, _) = "no" example: g(a, x, x) = g(y, b, z) - recursively ask a = y x = b x = z {y->a, x->b, x->z} ---> {y->a, x->b, z->b} example: mgu(x, f(x)) = {x->f(x)}

------------key computation step in prolog is *resolution*: a *clause* is either a fact or a rule. the head of a clause is either the fact itself (if a fact) or the head of the rule. the tail of a clause is nothing (if a fact) or the body of the rule. resolution(clause, list of goals to be proven): clause = renameVars(clause) // rename all vars to fresh names s = MGU(head(clause), head(goals)) return s(append(tail(clause), tail(goals))) example: 1. 1.5. 2. 3. 4. p(f(Y)) :- q(Y), r(Y). p(b). q(h(Z)) :- t(Z). r(h(a)). t(a).

resolution(p(f(Y)) :- q(Y), r(Y), [p(X)]) MGU(p(f(Y)), p(X)) = {X->f(Y)} return [q(Y), r(Y)] resolution(q(h(Z)) :- t(Z), [q(Y), r(Y)]) MGU(q(h(Z)), q(Y)) = {Y -> h(Z)} return [t(Z), r(h(Z))] resolution(t(a), [t(Z), r(h(Z))]) MGU(t(a), t(Z)) = {Z->a} return [r(h(a))] resolution(r(h(a)), [r(h(a))]), MGU(r(h(a)), r(h(a))) = {} return [] ------------rev(Res, [2,1]) 1. No 2. rev(T, X), app(X, [H], [2,1]) 1. app([], [H], [2,1]) (eventually "no") 2. rev(T',X'), app(X', [H'], X), app(X, [H], [2,1]) 1. app([],[H'],X), app(X,[H],[2,1]) (eventually "yes")