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:
- Class
TransparentColor
inherits the instance (i.e. non-static) fieldsred
,green
, andblue
from classColor
. This means that for each instance of classTransparentColor
, the computer stores four values: the values for fieldsred
,green
,blue
, andtransparency
. - Java requires that a constructor first initialize the superclass fields. Typically, this means calling a superclass constructor, using the special syntax
super(...)
. If no explicitsuper(...)
call is written at the start of a constructor, an implicit zero-argumentsuper()
call is inserted by the compiler. If the superclass has no zero-argument constructor, the compiler reports an error. - The implementation of method
equals
in classTransparentColor
reuses the implementation ofequals
in classColor
by calling it using the specialsuper.M(...)
syntax. In contrast to regular instance method calls,super
calls are statically bound: thesuper.equals
call in classTransparentColor
is always bound to methodequals
in classColor
. - Since method
equals
in classColor
checks thatthis.getClass()
equalsother.getClass()
, we know that after the call of this method in methodequals
of classTransparentColor
returnstrue
,other
is an instance of of classTransparentColor
so we can safely cast it to typeTransparentColor
. - Methods
getHue
,getSaturation
, andgetValue
are simply inherited by classTransparentColor
from classColor
. This means that callinggetHue
on aTransparentColor
object O executes methodgetHue
from classColor
on O. This, in turn, means that inside such an execution of methodgetHue
,this
will refer to an instance of classTransparentColor
.
Fields are hidden, not overridden!
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
.