Abstraction
Table of contents
- Abstraction
- When a class must be abstract?
- Can
final
classes beabstract
? - Can
abstract
classes haveprivate
constructors?
Abstraction
A box can be empty or non-empty. In fact, both the LightBox
and the HeavyBox
classes have the isEmpty()
method which does the same thing for both types of boxes. Given that all boxes can be empty (or non-empty), we can move the isEmpty()
method to the Box
class.
The Box
class does not have enough information to determine whether it is empty or not. The sub-classes use different mechanism to determine whether they are empty or not.
- The
LightBox
make use of thespace
(Space
enum) property - The
HeavyBox
delegates this to theitems
(List
’sisEmpty()
method) property
This means that while all boxes can be empty (or non-empty), the Box
class cannot answer the question, isEmpty()
. The Box
class needs to have a method for which it does not have an implementation. Methods that do not have an implementation, or a body, are referred to as abstract methods, as shown next.
public abstract boolean isEmpty();
The method shown above does not have a body and a semi-colon (;
) is used instead of curly brackets ({}
). The full example is shown next.
package demo;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Strings.nullToEmpty;
public abstract class Box {
private BoxForm form = BoxForm.CLOSED;
private String label = "No Label";
public Box() { /* ... */ }
public Box( final BoxForm form ) { /* ... */ }
public void open() { /* ... */ }
public void close() { /* ... */ }
public boolean isOpen() { /* ... */ }
public String getLabel() { /* ... */ }
public void changeLabelTo( final String label ) { /* ... */ }
private static boolean isValidLabel( final String label ) { /* ... */ }
public abstract boolean isEmpty();
@Override
public String toString() { /* ... */ }
public enum BoxForm { /* ... */ }
}
The isEmpty()
method needs to be abstract
as while a box can be empty or not, the Box
class does not know how to answer this question.
Shapes are a good analogy. All shapes have an area, but we cannot compute the area of shape, as shape is abstract. We cannot draw a shape as shape is abstract. Yet we know that shapes have an area. We can compute the area of a square or a circle, but we cannot compute the area of a shape.
When a class must be abstract?
A class can be abstract
public abstract class A { }
A class that has abstract methods must be abstract
public abstract class A { public abstract void m(); }
A class that inherits abstract methods that are not implemented must be abstract
public abstract class A { public abstract void m(); } public abstract class B extends A { /* Inherited abstract methods not implemented */ }
Note that class
B
inherits an abstract method from classA
. The abstract methodm()
is not implemented and thus, classB
must be abstract.
Can final
classes be abstract
?
A class that is marked final
cannot be extended. Therefore, a final
class cannot be abstract
. Either final
or abstract
, but not both.
A final
class must be concrete. A concrete class is the opposite for an abstract
class.
Can abstract
classes have private
constructors?
Yes, abstract
classes can have private
constructors. This might not make much sense, because how can we extend an abstract
class if all of its constructors are private
? There are cases where we want to limit the types of objects we want to support, and still have a class hierarchy.
Consider the following example.
package demo;
public abstract class Temperature {
private Temperature() { }
public abstract boolean isTooCold();
public abstract boolean isTooHot();
public static Temperature withFahrenheit( final double fahrenheit ) { /* ... */ }
public static Temperature withCelsius( final double celsius ) { /* ... */ }
public static Temperature withKelvin( final double kelvin ) { /* ... */ }
private static class Fahrenheit extends Temperature { /* ... */ }
private static class Celsius extends Temperature { /* ... */ }
private static class Kelvin extends Temperature { /* ... */ }
}
According to wikipedia, “*temperature* is a physical property of matter that quantitatively expresses hot and cold”. The Temperature
class is an abstract
class that defines two abstract
methods, isTooCold()
and isTooHot()
. The temperature can be measured (or represented) in Fahrenheit, Celsius or other units.
Similar to the shapes, we cannot create a temperature without specifying its scale. The above implementation defines three variants, all of which extend the Temperature
class. The above example looks a lot like enums, and that is almost correct.
Like enums, we can only have the types defined, Fahrenheit
, Celsius
and Kelvin
and we cannot add new types (outside from the Temperature
class). The following will not work.
public class MyNewTemperatureType extends Temperature {
@Override
public boolean isTooCold() {
return false;
}
@Override
public boolean isTooHot() {
return false;
}
}
This is a form of subtypes control that while enables subclasses, only the defined subclasses are allowed. The Temperature
class cannot be extended by an external class, which prevents some funny code added to the application. Different from enums, we can have multiple instances of each type.