|
|
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 3 of 4
Base ref = new Base(); System.out.println( ref.p() ); // REJECT
The second statement calls method p() through the type Base variable ref. The type-checker rejects that expression since type Base does not have an operation p(), even though the attached object possesses implementation code for method p(). The type-checker rigidly enforces that the declared type of ref cannot call method p(). Now consider the following definition of class Derived:
public class Derived
extends Base
{
public String m1()
{
return "Derived.m1";
}
public String m3()
{
return "Derived.m3()";
}
}
Through the extends clause, Derived both subclasses and subtypes Base. As a subclass, Derived performs module extension by overriding the implementation of method m1() and by declaring and implementing a new method, m3(). Derived automatically reuses the Base implementation of method m2(String).
The class text also declares a new type, Derived, as a subtype of Base. By building on Base, Derived contains the two operations m1() and m2(String) as well as the new operation m3(). As a subtype, Derived performs type specialization; that is, type Derived is a special kind of Base. Importantly, the type-checker allows the legal substitution of any type Derived variable or program expression where expecting type Base. For example, the type-checker accepts the program statement
Base ref = new Derived();
The variable ref has type Base and the expression new Derived() has type Derived. The type-checker examines the type hierarchy to verify type Derived conforms to type Base. Note that type conformance relies only on the type hierarchy defined through class inheritance. I'll touch on that important
fact again when investigating types defined via the interface concept.
Curiously, Java interfaces offer perhaps the clearest distinction between type and class. An interface, quite simply, declares a type, deferring partial or full implementation to abstract or concrete classes. At first glance, that may seem to be the same as a class with no implementation. In fact, Java interfaces are often compared to C++ pure abstract classes. That misguided comparison, however, clouds an important Java language contribution. An interface not only separates type declaration from module implementation, it also facilitates the separation of type inheritance and implementation inheritance.
Java clearly distinguishes between these two kinds of inheritance. The Java language specification restricts module extension
to single implementation inheritance, whereas type specialization supports multiple interface inheritance. While code reuse
through module extension requires class inheritance via the extends clause, type specialization results either from class inheritance or from interface inheritance via the implements clause.
When using the extends clause, type specialization may be viewed as a secondary result of module extension. With the implements clause, however, type specialization takes front and center. The Java interface keyword declares a type and the implements keyword guarantees that a class either implements or defers the implementation of all operations for each type specified
in the implements clause. That guarantees that objects instantiated from concrete classes possess implementation code for all the operations
of each type specified in the implements clause.
Consider the following interface and class definitions:
interface Foo
{
String m3();
}
public class Derived
extends Base
implements Foo
{
public String m1()
{
return "Derived.m1";
}
public String m3()
{
return "Derived.m3()";
}
}
Interface Foo declares a new type, Foo, with a single operation, m3(). The definition of class Derived differs from before only by the addition of the implements Foo clause. As previously demonstrated, by extending Base, class Derived inherits implementation code from class Base and type Derived inherits the operations of type Base. The additional implements Foo clause means type Derived also subtypes Foo. Thus you achieve multiple type inheritance: type Derived subtypes type Base and type Foo. Let's examine the ramifications of these type definitions.
Derived derived = new Derived(); Foo foo = derived; Base base = derived; foo = base; // REJECT
The first statement attaches a type Derived variable to a type Derived object-creation expression. The second statement attaches a variable of type Foo to the created object. The type-checker enforces conformance between the type Derived variable and the type Foo variable by verifying that Derived actually subtypes Foo. The third statement similarly passes type-checking. Note that these conformance checks consider the variable type, not the attached object type. In fact, later I'll explore the notion that objects do not have type. The type-checker rejects the fourth statement
since Foo does not subtype Base, even though foo and base point to the same object. Incompatible variable types prevent this attempt to attach a variable to the very object to which
it is already attached.
Now let's take a look at method invocation via the different variable types.
Derived derived = new Derived(); Foo foo = derived; Base base = derived; // Section 1: call m3() System.out.println( derived.m3() ); System.out.println( foo.m3() ); System.out.println( base.m3() ); // REJECT // Section 2: call m1() System.out.println( derived.m1() ); System.out.println( foo.m1() ); // REJECT System.out.println( base.m1() );
Section 1 calls method m3() through three different reference variable types. Since type Base does not possess operation m3(), the type-checker rejects the section's third statement. Similarly, the type-checker rejects the second statement in Section
2 since type Foo does not possess operation m1(). Remarkably, the three variables remain attached to the same object for every method call, and that object has implementation
for both the m1() and m3() methods. In the rejected statements, the type-checker overrules the underlying Derived object's ability to perform the specified method call by enforcing variable type conformance.
In Java Language Specification, James Gosling et al. proclaim that, "Variables have type, objects have class." That surprising statement contradicts the common practice of referring to an object's type. Gosling further notes, as discussed in the article, that types exist at compile time and objects exist at runtime.