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
.