Consider the following class Color, whose instances represent colors defined by their red, green, and blue components:
public class Color {
public final int red, green, blue;
public Color(int red, int green, int blue) {
this.red = red;
this.green = green;
this.blue = blue;
}
public int getHue() { /* ... */ }
public int getSaturation() { /* ... */ }
public int getValue() { /* ... */ }
@Override
public String toString() {
return "rgb(" + red + ", " + green + ", " + blue + ")";
}
@Override
public boolean equals(Object other) {
return
other.getClass() == getClass() &&
((Color)other).red == red &&
((Color)other).green == green &&
((Color)other).blue == blue;
}
@Override
public int hashCode() {
return Objects.hash(red, green, blue);
}
}
Often, this class is sufficient. But sometimes, we need to additionally store a transparency value. Instead of duplicating the existing functionality of Color,
we can declare class TransparentColor as a subclass of Color:
public class TransparentColor extends Color {
public final int transparency;
public TransparentColor(int red, int green, int blue, int transparency) {
super(red, green, blue);
this.transparency = transparency;
}
@Override
public String toString() {
return
"rgba(" + red + ", " + green + ", " + blue + ", " + transparency + ")";
}
@Override
public boolean equals(Object other) {
return
super.equals(other) &&
((TransparentColor)other).transparency == transparency;
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), transparency);
}
}
Notice the following:
TransparentColor inherits the instance (i.e. non-static) fields red, green, and blue from class Color.
This means that for each instance of class TransparentColor, the computer stores four values: the values for fields red, green, blue, and transparency.super(...).
If no explicit super(...) call is written at the start of a constructor, an implicit zero-argument super() call is inserted by the compiler. If the superclass has
no zero-argument constructor, the compiler reports an error.equals in class TransparentColor reuses the implementation of equals in class Color by calling it using the special
super.M(...) syntax. In contrast to regular instance method calls, super calls are statically bound: the super.equals call in class TransparentColor is
always bound to method equals in class Color.equals in class Color checks that this.getClass() equals other.getClass(), we know that after the call of this method in method equals of class TransparentColor returns true,
other is an instance of of class TransparentColor so we can safely cast it to type TransparentColor.getHue, getSaturation, and getValue are simply inherited by class TransparentColor from class Color. This means that calling
getHue on a TransparentColor object O executes method getHue from class Color on O. This, in turn, means that inside such an execution of method getHue,
this will refer to an instance of class TransparentColor.If a class D declares a field with the same name F as a field it inherits from its superclass C, the inherited field is hidden, not overridden! In that case, each object of class D has two fields named F: the one declared in class C, and the one declared in class D. When F is mentioned in class C, the field declared in class C is accessed; when F is mentioned in class D, the field declared in class D is accessed. You can access the field of class C from class D using notation super.F.