6 Object-oriented Programming

6.1

(a) and (b)

The extends clause is used to specify that a class extends another class. A subclass can be declared abstract regardless of whether the superclass was declared abstract. Private, overridden, and hidden members from the superclass are not inherited by the subclass. A class cannot be declared both abstract and final, since an abstract class needs to be extended to be useful and a final class cannot be extended. The accessibility of the class is not limited by the accessibility of its members. A class with all the members declared private can still be declared public.

6.2

(b) and (e)

Inheritance defines an is-a relation. Aggregation defines a has-a relation. The Object class has a public method named equals, but it does not have any method named length. Since all classes are subclasses of the Object class, they all inherit the equals() method. Thus, all Java objects have a public method named equals. In Java, a class can only extend a single superclass, but there is no limit on how many classes can extend a superclass.

6.3

(b) and (c)

A subclass need not redefine all the methods defined in the superclass. It is possible for a subclass to define a method with the same name and parameters as a method defined by the superclass, but then the return type should also be the same. This is called method overriding. A subclass can define a field that can hide a field defined in a superclass. Two classes cannot be the superclass of each other.

6.4

(a), (b), and (d)

Bar is a legal subclass of Foo that overrides the method g(). The statement a.j = 5 is not legal, since the member j in class Bar cannot be accessed through a Foo reference. The statement b.i = 3 is not legal either, since the private member i cannot be accessed from outside of the class Foo.

6.5

(a)

A method can be overridden by defining a method with the same signature (i.e., name and parameter list) and return type as the method in a superclass. Only instance methods that are accessible by their simple name can be overridden. A private method, therefore, cannot be overridden in subclasses, but the subclasses are allowed to define a new method with exactly the same signature. A final method cannot be overridden. An overriding method cannot exhibit behavior that contradicts the declaration of the original method. An overriding method, therefore, cannot return a different type of value or declare that it throws more exceptions than the original method in the superclass.

6.6

(g)

It is not possible to invoke the doIt() method in A from an instance method in class C. The method in C needs to call a method in a superclass two levels up in the inheritance hierarchy. The super.super.doIt() strategy will not work, since super is a keyword and cannot be used as an ordinary reference, nor accessed like a field. If the member to be accessed had been a field, the solution would be to cast the this reference to the class of the field and use the resulting reference to access the field. Field access is determined by the declared type of the reference, whereas the instance method to execute is determined by the actual type of the object denoted by the reference.

6.7

(e)

The code will compile without errors. None of the calls to a max() method are ambiguous. When the program is run, the main() method will call the max() method in C with the parameters 13 and 29. This method will call the max() method in B with the parameters 23 and 39. The max() method in B will in turn call the max() method in A with the parameters 39 and 23. The max() method in A will return 39 to the max() method in B. The max() method in B will return 29 to the max() method in C. The max() method in C will return 29 to the main() method.

6.8

(c)

The simplest way to print the message in the class Message would be to use System. out.println(msg.text). The main() method creates an instance of MyClass, which results in the creation of a Message instance. The field msg denotes this Message object in MySuperclass and is inherited by the MyClass object. Thus, the message in the Message object can be accessed directly by msg.text in the print() method of MyClass.

6.9

(e)

The class MySuper does not have a default constructor. This means that constructors in subclasses must explicitly call the superclass constructor to provide the required parameters. The supplied constructor accomplishes this by calling super(num) in its first statement. Additional constructors can accomplish this either by calling the superclass constructor directly using the super() call, or by calling another constructor in the same class using the this() call, which in turn calls the superclass constructor. (a) and (b) are not valid since they do not call the superclass constructor explicitly. (d) fails since the super() call must always be the first statement in the constructor body. (f) fails since the super() and this() calls cannot be combined.

6.10

(b)

In a subclass without any declared constructors, the implicit default constructor will call super(). The use of the super() and this() statements are not mandatory as long as the superclass has a default constructor. If neither super() nor this() is declared as the first statement in the body of a constructor, then super() will implicitly be the first statement. A constructor body cannot have both a super() and a this() statement. Calling super() will not always work, since a superclass might not have a default constructor.

6.11

(d)

The program will print 12 followed by Test. When the main() method is executed, it will create a new instance of B by giving "Test" as argument. This results in a call to the constructor of class B that has one String parameter. The constructor does not explicitly call any superclass constructor, but instead the default constructor of the superclass A is called implicitly. The default constructor of class A calls the constructor in A that has two String parameters, giving it the arguments "1", "2". This constructor calls the constructor with one String parameter, passing the argument "12". This constructor prints the argument. Now the execution of all the constructors in A is completed, and execution continues in the constructor of B. This constructor now prints the original argument "Test" and returns to the main() method.

6.12

(b) and (c)

Interfaces do not have any implementations and only permit multiple interface inheritance. An interface can extend any number of other interfaces and can be extended by any number of interfaces. Fields in interfaces are always static and method prototypes in interfaces are never static.

6.13

(a), (c), and (d)

Fields in interfaces declare named constants, and are always public, static, and final. None of these modifiers are mandatory in a constant declaration. All named constants must be explicitly initialized in the declaration.

6.14

(a) and (d)

The keyword implements is used when a class inherits from an interface. The keyword extends is used when an interface inherits from another interface or a class inherits from another class.

6.15

(e)

The code will compile without errors. The class MyClass declares that it implements the interfaces Interface1 and Interface2. Since the class is declared abstract, it does not need to supply implementations for all the method prototypes defined in these interfaces. Any non-abstract subclasses of MyClass must provide the missing method implementations. The two interfaces share a common method prototype void g(). MyClass provides an implementation for this prototype that satisfies both Interface1 and Interface2. Both interfaces provide declarations of constants named VAL_B. This can lead to an ambiguity when referring to VAL_B by its simple name from MyClass. The ambiguity can be resloved by using fully qualified names: Interface1.VAL_B and Interface2.VAL_B. However, there are no problems with the code as it stands.

6.16

(a) and (c)

Declaration (b) fails since it contains an illegal forward reference to its own named constant. The field type is missing in declaration (d). Declaration (e) tries illegally to use the protected modifier, even though named constants always have public accessibility.

6.17

(c)

The program will throw a java.lang.ClassCastException in the assignment at (3) when run. The statement at (1) will compile since the assignment is done from a subclass reference to a superclass reference. The cast at (2) assures the compiler that arrA will refer to an object that can be referenced by arrB. This will work when run since arrA will refer to an object of type B[]. The cast at (3) will also assure the compiler that arrA will refer to an object that can be referenced by arrB. This will not work when run since arrA will refer to an object of type A[].

6.18

(d)

Line (4) will cause a compile-time error since it attempts to assign a reference value of a supertype object to a reference of a subtype. The type of the source reference value is MyClass and the type of the destination reference is MySubclass. Lines (1) and (2) will compile since the reference is assigned a reference value of the same type. Line (3) will also compile since the reference is assigned a reference value of a subtype.

6.19

(e)

Only the assignment I1 b = obj3 is valid. The assignment is allowed since C3 extends C1, which implements I1. Assignment obj2 = obj1 is not legal since C1 is not a subclass of C2. Assignments obj3 = obj1 and obj3 = obj2 are not legal since neither C1 nor C2 is a subclass of C3. Assignment I1 a = obj2 is not legal since C2 does not implement I1. Assignment I2 c = obj1 is not legal since C1 does not implement I2.

6.20

(b)

The statement would be legal at compile time, since the reference x might actually refer to an object of the type Sub. The cast tells the compiler to go ahead and allow the assignment. At runtime, the reference x may turn out to denote an object of the type Super instead. If this happens, the assignment will be aborted and a ClassCastException will be thrown.

6.21

(c)

Only A a = d is legal. The reference value in d can be assigned to a since D implements A. The statements c = d and d = c are illegal since there is no subtype-supertype relationship between classes C and D. Even though a cast is provided, the statement d = (D) c is illegal. The object referred to by c cannot possibly be of type D, since D is not a subclass of C. The statement c = b is illegal since assigning a reference value of a reference of type B to a reference of type C requires a cast.

6.22

(a), (b), and (c)

The program will print A, B, and C when run. The object denoted by reference a is of type C. The object is also an instance of A and B, since C is a subclass of B and B is a subclass of A. The object is not an instance of D.

6.23

(b)

The expression (o instanceof B) will return true if the object referred to by o is of type B or a subtype of B. The expression (!(o instanceof C)) will return true unless the object referred to by o is of type C or a subtype of C. Thus, the expression (o instanceof B) && (!(o instanceof C)) will only return true if the object is of type B or a subtype of B that is not C or a subtype of C. Given objects of classes A, B, and C, this expression will only return true for objects of class B.

6.24

(a)

The program will print all of I, J, C, and D when run. The object referred to by reference x is of class D. Class D extends class C and class C implements interface I. This makes I, J, and C supertypes of class D. A reference of type D can be cast to any of its supertypes and is, therefore, an instanceof these types.

6.25

(e)

The program will print 2 when System.out.println(ref2.f()) is executed. The object referenced by ref2 is of class C, but the reference is of type B. Since B contains a method f(), the method call will be allowed at compile time. During execution it is determined that the object is of class C, and dynamic method lookup will cause the overridden method in C to be executed.

6.26

(c)

The program will print 1 when run. The f() methods in A and B are private and are not accessible by the subclasses. Because of this, the subclasses cannot overload or override these methods, but simply define new methods with the same signature. The object being called is of class C. The reference used to access the object is of type B. Since B contains a method g(), the method call will be allowed at compile time. During execution it is determined that the object is of class C, and dynamic method lookup will cause the overridden method g() in B to be called. This method calls a method named f(). It can be determined during compilation that this can only refer to the f() method in B, since the method is private and cannot be overridden. This method returns the value 1, which is printed.

6.27

(c) and (d)

The code as it stands will compile without error. The use of inheritance in this code is not justifiable since, conceptually, a planet is-not-a star. The code will fail if the name of the field starName is changed in the Star class since the subclass Planet tries to access it using the name starName. An instance of Planet is not an instance of HeavenlyBody. Neither Planet nor Star implements HeavenlyBody.

6.28

(b)

The code will compile without error. The use of aggregation in this code is justifiable. The code will not fail to compile if the name of the field starName is changed in the Star class, since the Planet class does not try to access the field by name, but instead uses the public method describe() in the Star class to do that. An instance of Planet is not an instance of HeavenlyBody since it neither implements HeavenlyBody nor extends a class that implements HeavenlyBody.