Lecture Notes: Java's Object Model

Related Reading: Ch. 7.1 - 7.2

Types and Subtypes

Variables have a declared type, and each variable stores a particular value.

Every type in Java is one of the following.

  1. A primitive type (e.g., int)
  2. A class type (e.g., Rectangle)
  3. An interface type (e.g., Shape)
  4. An array type (e.g., int[], String[], Shape[])
  5. The null type (a technicality)

Every value in Java is one of the following.

  1. A value of a primitive type (e.g., 13)
  2. A reference to an object of a class (e.g., new Rectangle(5, 10, 20, 30))
  3. A reference to an array (e.g., new int[]{1,3,5,7})
  4. null

Subtype relationship between types: by substitution principle, a variable of a subtype can be used in place of its supertype. Rules are that S is a subtype of T if:

  1. S and T are the same type
    (e.g. Rectangle is formally a subtype of itself)

  2. S and T are both class types and S is a direct or indirect subtype of T.
    Employee
       ^
       |
    Manager
       ^
       |
    VicePresident
    In this scenario,
    Manager is a subtype of Employee.
    VicePresident is a subtype of both Manager and Employee.

  3. S and T are both interface types and S is a direct or indirect subinterface of T.
    EventListener      
       ^
       |
    MouseListener
       ^
       |
    MouseInputListener
    In this scenario,
    MouseListener is a subtype of EventListener.
    MouseInputListener is a subtype of both MouseListiner and EventListener.

  4. S is a class type and T is an interface type, and S or one of its superclasses implements the interface type T or one of its subinterfaces.
    EventListener      
       ^
       |
    MouseListener      <--  MouseAdapater
       ^                         ^
       |                         |
    MouseInputListener <--  MouseInputAdapter
                                 ^
                                 |
                            BasicSliderUI.TrackListener
    
    For example, the BasicSliderUI.TrackListener class is a subtype of the EventListener interface.

  5. S and T are both array types and the component type of S is a subtype of the component type of T.
    For example, JButton[] is a subtype of Component[].

  6. S is not a primitive type and T is Object.
    Thus, any class is a subtype of Object, any interface is a subtype of Object (including arrays, such as int[]).

  7. S is an array type and T is the type Cloneable or Serializable.
    Arrays automatically get these interface traits, even though not explicitly declared.

  8. S is the null type and T is not a primitive type.
    Note that a variable of any type other than a primitive can take on the null value.


A note about Array Types

By above, the type Rectangle[] is a subtype of Shape[]. But this means we can do the following:

Rectangle[] ra = new Rectangle[10];
ra[0] = new Rectangle(1,2,3,4);
Shape[] sa = ra;                    // legal
But given the declaration of sa, should it be legal syntax to do
sa[0] = new Polygon(...);
Syntactically that is legal, but it would then leave the array ra referencing an object that is not of the proper type. Java handles this by having an ArrayStoreException at runtime; each underlying array knows its component type.


Enumerated Types

public enum Size { SMALL, MEDIUM, LARGE };

Technically, these are implemented in Java as classes, and in particular as subtypes of an Enum class.


Type Inquiry

Can perform runtime checks to determine more about the true type of a value.

Object x = ...
if (x instanceof Shape) { ... }

The instanceof operator allows for checks of the formal subtype relationships.

That is different from asking what the true type is of a value. That reflections is also possible using the syntax

Class c = x.getClass();     // valid for x having any non-primitive type
Formally, this is supported by Object.getClass().


The Object Class

Some supported methods of Object.


Equality Testing

For all non-primitive types, the expression x == y determines whether x and y are references to the same object.

That is not necessarily the same notion as equivalence in the context of some type. For example, two distinct String instances, might represent the identical sequence of characters. The Object.equals method is used to support a syntax x.equals(y) for all non-primitive types. By default, Object.equals defines equivalence to be the same as identity

public boolean equals(Object obj) {
  return this == obj;
}
however other classes are free to override this behavior to provide better semantics for equivalence. For example, String.equals defines equality based instances having the identical sequence of characters.

Requirements for a consistent equals method

We will look at a detailed example next time...


Last modified: Monday, 10 October 2011