Inheriting, Overriding, and Overloading Methods

A subinterface inherits all the methods declared in its superinterfaces. If a declared method in a subinterface has the same signature (name and parameter list) as an inherited method and the same, or covariant, return type, then the new declaration overrides any and all existing declarations. Overriding in interfaces, unlike overriding in classes, has no semantic effectthe interface effectively contains multiple declarations of the same method, but in any one implementing class there can only be one implementation of that method.

Similarly, if an interface inherits more than one method with the same signature, or if a class implements different interfaces containing a method with the same signature, there is only one such method. The implementation of this method is ultimately defined by the class implementing the interfaces, and there is no ambiguity there. If the methods have the same signature but different return types, then one of the return types must be a subtype of all the others, otherwise a compile-time error occurs. The implementation must define a method that returns that common subtype.

The real issue is whether a single implementation of the method can honor all the contracts implied by that method being part of the different interfaces. This may be an impossible requirement to satisfy in some circumstances. For example:

interface CardDealer
{

void draw(); // flip top card
void deal(); // distribute cards
void shuffle();
}
interface GraphicalComponent
{

void draw(); // render on default device
void draw(Device d); // render on 'd'
void rotate(int degrees);
void fill(Color c);
}

interface GraphicalCardDealer
CardDealer, GraphicalComponent { }



Here it is difficult to write an implementation of draw() that can satisfy the two different contracts independently. If you try to satisfy them simultaneously, you are unlikely to achieve the desired results: flipping a card each time the screen gets repainted.

As with overriding in class extension, the overriding method is not permitted to throw more checked exceptions than the method it overrides. If two or more method declarations are inherited, without overriding, and differ in the exceptions they throw, then the implementation of that method must satisfy all the throws clauses of those declarations. Again the main issue is whether such distinct methods can have a single implementation that honors all contracts.



Working with Interfaces
The first decision to make is whether having attributes is reflected in the type of the object. An object could, if you chose, contain a set of attributes and allow programmers access to that set. Or you could say that being able to store attributes on an object is a part of its type and so should be part of the type hierarchy. Both positions are legitimate. We believe that representing the ability to hold attributes in the type hierarchy is most useful. We will create an Attributed type to be used for objects that can be attributed by attaching Attr objects to them.

To create an Attributed type you could define a class that would form the superclass for all attributed objects. But then programmers must decide whether to inherit from Attributed or from some other useful class. Instead we make Attributed into an interface:

public interface Attributed
{

void add(Attr newAttr);
Attr find(String attrName);
Attr remove(String attrName);
java.util.Iterator attrs();
}



This interface declares four methods: one for adding a new attribute to an Attributed object; one for finding whether an attribute of a given name has been added to that object; one for removing an attribute from an object; and one for accessing all of the attributes currently attached to the object.

When we add an attribute to an object, that object considers itself the owner of that Attr instance until it is removed. If the attribute has its value changed or is shared among a number of objects, then the expectation is that the programmer makes such changes and performs such sharing in a manner that makes sense to the application. If the programmer is not to be trusted in this, then methods should specify that they make defensive copies of parameters, and/or return values, such that no harmful changes can occur.


Implementing Interfaces
Interfaces describe contracts in a pure, abstract form, but an interface is interesting only if a class implements it.

Some interfaces are purely abstractthey do not have any useful general implementation but must be implemented afresh for each new class. Most interfaces, however, may have several useful implementations. In the case of our Attributed interface, we can imagine several possible implementations that use various strategies to store a set of attributes.

One strategy might be simple and fast when only a few attributes are in a set; another one might be optimized for attribute sets that are queried more often than they are changed; yet another design might be optimized for sets that change frequently. If there were a package of various implementations for the Attributed interface, a class might choose to implement the Attributed interface through any one of them or through its own implementation.

First, here is the AttributedImpl class:

import java.util.*;

class AttributedImpl implements Attributed, Iterable
{

protected Map attrTable =
new HashMap();

public void add(Attr newAttr)
{

attrTable.put(newAttr.getName(), newAttr);
}

public Attr find(String name)
{

return attrTable.get(name);
}

public Attr remove(String name)
{

return attrTable.remove(name);
}

public Iterator attrs()
{

return attrTable.values().iterator();
}

public Iterator iterator()
{

return attrs();
}
}



The initializer for attrTable creates a HashMap object to hold attributes. This HashMap object does most of the actual work. The HashMap class uses the key object's hashCode method to hash any object it is given as a key. No explicit hash method is needed since String already provides a good hashCode implementation.

When a new attribute is added, the Attr object is stored in the hash map under its name, and then you can easily use the hash map to find and remove attributes by name. The attrs method returns the Iterator for the hash map's values, giving access to all the attributes of the current object.

We chose to make this implementation of Attributed also implement Iterable, as the attributes are the only things an AttributedImpl contains. To do this, we had to define the iterator method to return the same value as the attrs method.

Using an Implementation
You can use an implementing class like AttributedImpl by simply extending the class. This is the simplest tool when it is available because all the methods and their implementations are inherited. But if you need to support more than one interface or extend a different class, you must use a different approach. The most common approach is to create an object of an implementing class and forward all the methods of the interface to that object, returning any valuesthis is often called composition.

In composition and forwarding, each method in the class that is inherited from the interface invokes the implementation from another object and returns the result. Here is an implementation of the Attributed interface that uses an AttributedImpl object to build an attributed version of our previously defined celestial body class Body:

import java.util.Iterator;

class AttributedBody extends Body
implements Attributed
{
private AttributedImpl attrImpl = new AttributedImpl();

public AttributedBody() {
super();
}

public AttributedBody(String name, Body orbits) {
super(name, orbits);
}

// Forward all Attributed methods to the attrImpl object

public void add(Attr newAttr)
{ attrImpl.add(newAttr); }
public Attr find(String name)
{ return attrImpl.find(name); }
public Attr remove(String name)
{ return attrImpl.remove(name); }
public Iterator attrs()
{ return attrImpl.attrs(); }
}


The declaration that AttributedBody extends Body and implements Attributed defines the contract of AttributedBody. The implementations of all Body's methods are inherited from the Body class itself. Each method of Attributed is implemented by forwarding the invocation to the AttributedImpl object's equivalent method, returning its value (if any). This also means that you must add a field of type AttributedImpl to use in the forwarding methods and initialize that field to refer to an AttributedImpl object.


We Are Founder..