Vogliamo:
class Point { int x; int y; Point (int x1, int y1) { x = x1; y = y1; } }
class ColouredPoint extends Point { String colour; ColouredPoint (int x, int y, String colour) { this.x = x; this.y = y; this.colour = colour; } }
ColouredPoint pt = new ColouredPoint(3, 4, "red"); pt.x += 1; pt.colour = "blue";
Point pt = new ColouredPoint(3, 4, "red"); pt.colour = "blue";
Point pt = new ColouredPoint(3, 4, "red"); ((ColouredPoint) pt).colour = "blue";
class Point { ... int distanceToPoint() { return (int) Math.sqrt(x * x + y * y); } ... }
(new Point(3, 4)) instanceof Object è vero
(new ColouredPoint(3, 4, "true)) instanceof Point è vero
(new ColouredPoint(3, 4, "true)) instanceof ColouredPoint è vero
(new Point(3, 4)) instanceof ColouredPoint è falso
((Point) (new ColouredPoint(3, 4, "red"))) instanceof ColouredPoint è vero
class Point { ... boolean equals(Object o) { if (o instanceof Point) { return (((Point) o).x == x) && (((Point) o).y == y); } else { return false; } } ... }
null instanceof Object è vero
String pt = null;
class ColouredPoint extends Point { ... boolean equals(Object o) { if (o instanceof ColouredPoint) { ColouredPoint pt = (ColouredPoint)o; return (pt.x == x) && (pt.y == y) && colour.equals(pt.colour); } else { return false; } } ... }
class ColouredPoint { ColouredPoint (int x, int y, String colour) { super(x, y); this.colour = colour; } boolean equals(Object o) { if (o instanceof ColouredPoint) { return super.equals(o) && colour.equals(((ColouredPoint) o).colour); } else { return false; } } }
class A { A(){ } int method() { return 1; } } class B extends A { B() { } }Tutto ciò che può fare un oggetto di tipo A, anche un oggetto di tipo B può farlo.
Il contrario non è vero.
class A { A(){ int method() { return 1; } } class B extends A { B() { int method2() { return 2; } }Tutto ciò che può fare un oggetto di tipo B, forse un oggetto di tipo A non può farlo.
Se c'è bisogno, si può anche cambiare il comportamento riscrivendo il metodo (overriding):
class A { A(){ } int method() { return 1; } } class B extends A { B() { } int method() { return 2; } }new A().method() vale sempre 1
Questo è vero anche quando l'oggetto è un parametro (overloading):
class C { C() { } int method(A a) { return 2; } }new C().method(new A()) è valido e vale 2.
class C { C() { } int method(Object a) { return 2; } int method(A a) { return 3; } }Il tipo A è più specifico di Object.
La scelta del metodo può non essere unica:
class C { C() { } int method(B a) { return 1; } } class D extends C { D() { } int method(A a) { return 2; } }Per new D().method(new B()), i valori 1 e 2 sono possibili. In questo caso, un errore (Ambiguous) viene rilevato a tempo di compilazione.
vero tipo o tipo dinamico (Run time type)
tipo nel programma o tipo statico (Compile time type)
Point pt = new ColouredPoint(3, 4, "red");Il tipo statico di pt è Point.
Il tipo dinamico di pt è ColouredPoint.
int method (Point pt) { ... }Il tipo statico di pt è Point.
Il tipo dinamico pt è sconosciuto.
Overriding: Tipo dinamico
object1.method(object2)
class A { int method(A a) { return 1; } int method(B a) { return 2; } } class B extends A { int method(A a) { return 3; } int method(B a) { return 4; } }e
A a1 = new A(); B a2 = new B(); A a3 = new B();a1.method(a1) vale 1
Con:
class A { int method(A a) { return 1; } } class B extends A { int method(A a) { return 3; } int method(B a) { return 4; } }e
A a1 = new A(); B a2 = new B(); A a3 = new B();a1.method(a1) vale 1
Con:
class A { int method(A a) { return 1; } int method(B a) { return 2; } } class B extends A { int method(B a) { return 4; } }e
A a1 = new A(); B a2 = new B(); A a3 = new B();a1.method(a1) vale 1
Con:
class A { int method(B a) { return 2; } } class B extends A { int method(A a) { return 3; } int method(B a) { return 4; } }e
A a1 = new A(); B a2 = new B(); A a3 = new B();a1.method(a1) vale NoSuchMethod
Con:
class A { int method(A a) { return 1; } int method(B a) { return 2; } } class B extends A { int method(A a) { return 3; } }e
A a1 = new A(); B a2 = new B(); A a3 = new B();a1.method(a1) vale 1
class Print { static String toString(Object o) { return "Object"; } static String toString(Point pt) { return "(" + pt.x + ", " + pt.y + ")"; } static String toString(ColouredPoint pt) { return "(" + pt.x + ", " + pt.y + ")" + ":" + colour; } }
Point pt = new Point(3, 4);Print.toString(pt) vale (3, 4)
ColouredPoint pt = new CoulouredPoint(3, 4, "red");Print.toString(pt) vale (3, 4):red
Point pt = new CoulouredPoint(3, 4, "red");Print.toString(pt) vale (3, 4)
Un'alternativa per Print
class Print { static String toString(Object o) { return "Object"; } static String toString(Point pt) { return "(" + pt.x + ", " + pt.y + ")"; } static String toString(ColouredPoint pt) { return toString((Point) pt) + ":" + colour; } }
class Obj { static void method (Point pt1, ColouredPoint pt2) { .. } static void method (ColouredPoint pt1, Point pt2) { .. } }
ColouredPoint pt = new ColouredPoint(3, 4, "red"); Obj.method(pt, pt);quale codice è selezionato? Errore Ambiguous.
class Point { ... String toString() { return "(" + x + ", " + y + ")"; } ... } class ColouredPoint extends Point { ... String toString(ColouredPoint pt) { return super.toString() + ":" + colour; } ... }
Point pt = new Point(3, 4);pt.toString() vale (3, 4)
ColouredPoint pt = new CoulouredPoint(3, 4, "red");pt.toString() vale (3, 4):red
Point pt = new CoulouredPoint(3, 4, "red");pt.toString() vale (3, 4):red
class Print { String toString(Point pt) { return "(" + pt.x + ", " + pt.y + ")"; } } class ColourPrint extends Print { String toString(Point pt) { return super.toString(pt) + ":black"; String toString(ColouredPoint pt) { return super.toString(pt) + ":" + colour; } }
Print pr = new ColourPrint(); Point pt = new CoulouredPoint(3, 4, "red");pr.toString(pt) vale (3, 4):black
Che cosa non va con:
class Point { ... boolean equals(Point pt) { return (pt.x == x) && (pt.y == y); } else { return false; } } ... } class ColouredPoint extends Point { ... boolean equals(ColouredPoint pt) { return super.equals(pt) && colour.equals(pt.colour); } else { return false; } } ... }Due oggetti diversi possono essere equals:
ColouredPoint pt1 = new ColouredPoint(3, 4, "red"); Point pt2 = new ColouredPoint(3, 4, "blue");pt1.equals(pt2) è vero
class A { static int getVal() { return 1; } } class B extends A { static int getVal() { return 2; } }Con
A a = new A(); B b = new B(); A c = new B();abbiamo che a.getVal() e c.getVal() vale 1 e che b.getVal() vale 2.
Un metodo non statico non può essere overriding con un metodo statico. Questo non è valido:
class A { int getVal() { return 1; } } class B extends A { static int getVal() { return 2; } }
class Tree { } class Node extends Tree { Tree right; Tree left; Node (Tree left, Tree right) { this.left = left; this.right = right; } } class Leaf extends Tree { String s; Leaf (String s) { this.s = s; } }new Node(new Node(new Leaf("a"), new Leaf("b")), new Leaf("c")) è un albero.
Adesso vogliamo calcolare il numero delle foglie di un albero. Questo non va bene:
class Node extends Tree { ... int getSize() { return left.getSize() + right.getSize(); } class Leaf extends Tree { ... int getSize() { return 1; } }Perché left è di tipo Tree e Tree non definisce il metodo getSize.
Soluzione:
abstract class Tree { abstract int getSize(); } class Node extends Tree { ... int getSize() { return left.getSize() + right.getSize(); } class Leaf extends Tree { ... int getSize() { return 1; } }
Non si può fare new su una class abstract.
class Node extends Tree { ... final int getSize() { ... } }Questo indica che tutte le class che estendono Node usano per getSize il metodo definito nella class Node. Una conseguenza è che con i metodi finali si può scegliere il metodo da eseguire staticamente. Dunque il codice dovrebbe essere più efficace (nessun overriding a tempo di esecuzione).
I campi con il modificatore final sono costanti:
class A { final float pi = 3.1415926; }In questo caso, è anche meglio dirlo static final:
class A { static final pi = 3.14151926; }
(new ColouredPoint[2]) instanceof Point[] è vero
Point[] pts = new ColouredPoint[10]; pts[0] = new ColouredPoint(3, 4, "blue"); ((ColouredPoint) pts)[0].color = "red";Problema:
Point[] pts = new ColouredPoint[10]; pts[0] = new Point(3, 4);o
class foo { void putFirst (Point[] pts) { pts[0] = new Point(3, 4); } void start () { ColouredPoint[] pt = new ColouredPoint[3]; putFirst(pt); } }Questi due esempi sono errati ma sono accettati dall'analisi statica dei tipi (static type-checking). L'errore è scoperto solo a tempo di esecuzione. Questo è l'unico caso in cui un errore di tipo viene rilevato a tempo di esecuzione.
D2. Scrivere un metodo maxLength che ritorna una delle stringhe che ha la massima lunghezza in un albero.
D3. Scrivere un metodo listOf che ritorna l'array contenente tutte le stringhe in un albero.
D4. Scrivere un metodo equals che prede un oggetto e verifica se l'albero è uguale all'oggetto.
D5*. Modificare le classe Tree, Node e Leaf in tale modo che gli alberi possono avere un numero arbitrario di figli. Aggiornare in conseguenza i metodi in, maxLength, listOf e equals.
D6*. Definire una classa astratta List e due sottoclassi Cons e Nil per rappresentare le liste di numeri interi tale che la lista composta dei numeri 1, 2 e 3 può essere creata tramite l'espressione new Cons(1, new Cons(2, new Cons(3, new Nil()))).
Aggiungere in List:
D7*. Definire una classa astratta Path e due sottoclassi Move e Select per rappresentare un camino in un albero tale che il camino new Move(2, new Move(1, new Select())) rappresenta il verso il primo figlio del secondo figlio dell'albero. Aggiungere in la classe Tree dell'esercizio C5:
D8*. Definire una classa astratta Multi e due sottoclassi Single e Empty per rappresentare dei multinsiemi sui numeri interi tale che il multinsieme {1, 1, 3, 3, 3} può essere creato tramite l'espressione new Single(1, 2, new Single(3, 3, new Empty())).
Aggiungere in Multi:
D9*. Definire una classa astratta Exp e tre sottoclassi Int, Plus e Mult per rappresentare le espressioni arimetiche tale che l'espressione (1 + 2) * 3 possa essere rappresentata come Exp a = new Mult(new Plus(new Int(1), new Int(2)), new Int(3))}.
Quindi aggiungere nella classe Exp: