Solutions to questions of lecture 7 by Kasper B. Graversen version 1 - 19/03-04 --------------------------------------------------------------------------------------- ex 1.1 A a = new A() : OK (trivial) A a = new B() : OK (since B is of type A) A a = new C() : NOT OK (since C is not of type A) B b = new A() : NOT OK (since A is not of type B) B b = new B() : OK (trivial) again.. think of types as contracts that certain methods and fields exists. So references of type B can only point objects which exist contains all the (method and field) signatures defined in B. A objects cannot guarantee to not break "the contract of type B" since it does not contain the method 'bar()'--hence a B reference cannot contain an object of type A. ex 2.1 8 and 8 is printed. The second answer may be surprising. The reason is that fields are looked up depending on the type of 'this', rather than what 'this' points to. This means that the 'late binding of this' has no effect. One could also say that field lookup is non-virtual always. ex 2.3 The reason we have to use the return type 'Object' is that the return type may not vary (not even covariantly!), hence we have to find a comon super type for the two types 'String' and 'Integer'. ex 2.4 8 and "hello world" is printed. The reason is that since method lookup is virtual, it is B's getX() which is called when calling print() on B instances--even though print() is only defined in A. ex 2.5 The idiom of getter/setter methods is incompatible with the language mechanism in Java called shadowing of fields. One cannot use the getter/setter idiom on shadowed fields, since it will always be the last defined field which will be returned! And unfortunately the compiler does not warn you if you make such a mistake. Only proper testing of the code (e.g. unit testing) can reveal such errors. ex 3.2 class Line extends Point { private Point end; public Line(int x1,int y1, int x2, int y2){ super(x1,x2); end = new Point(x2, y2); } public void move(int dx, int dy){ super.move(dx,dy); end.move(dx, dy); } } ex 3.3 class Line { private Point start; private Point end; public Line(int x1,int y1, int x2, int y2){ start = new Point(x1, y1); end = new Point(x2, y2); } public void move(int dx, int dy){ start.move(dx, dy); end.move(dx, dy); } } ex 3.4 a) Due to polymorphic references, reference Point r could point to both line and point objects and move them around. b) One has to make Points and Line have a comon super type (other than Object, since that we cannot 'move()' the our objects using an Object reference). A solution could be to introduce a comon interface for the two classes. interface Moveable { public void move(int dx, ind dy); } class Point implements Moveable {..} class Line implements Moveable {..} ex 4.2 One can indeed implement two interfaces containing the same method signature (eg 'void foo()'). However, one is in the situation that 'foo()' now means both. This may not always be meaningful; as we saw in the exercise, just because the person draws his gun, he need not move on the screen. ex 4.3 The Java compiler reports that the return types are incompatible. This is due to the fact that return types has to be invariant and that one cannot overload on return types. ex 4.4 The support for handling accidental name clashing is poor, maybe even inadequate. In e.g. C# one can implement two draw() one for each interface.. at first this sounds much more preferable. However, then Cowboy r = new Person(); and Drawable r = new Person(); r.draw(); r.draw(); means completely different things (which is opposite of Java).. hence the type of the reference suddenly decides the method to be called. Also one most probably (I'm not a C# coder) has to define a special foo() method for the case Person r = new Person(); r.draw(); or the compiler will raise an error. ex 5.1.1 : b ex 5.1.2 : b ex 5.1.3 : b ex 5.2 Since methods are virtual it is always the most specific method which is chosen no matter what type the refence to the object has. ex 5.3 Yes, you can make a forward call in B: void a_foo() { return super.foo(); } ex 6.1 Although code B is much shorter than A (which usually is good if both implementations has the same functionality), B has the problem, that one cannot substitute the implementation of the list without much more change in the code. Since all references in A uses the interface type, such a substitution is straight forward. ex 7 class StaticVirtualTest_A { static void bar() { System.out.print("A.bar()"); } } class StaticVirtualTest extends StaticVirtualTest_A { static void bar() { System.out.print("B.bar()"); } public static void main(String[] args) { StaticVirtualTest_A r1 = new StaticVirtualTest_A(); StaticVirtualTest_A r2 = new StaticVirtualTest(); StaticVirtualTest r3 = new StaticVirtualTest(); r1.bar(); r2.bar(); r3.bar(); } } "A.bar()","A.bar()","B.bar()" is printed.