When to cast interface types to class types in Java

Some background

  • A type cast—or simply a cast— is an explicit indication to convert a value from one data type to another compatible data type.
  • A Java interface contains publicly defined constants and the headers of public methods that a class can define.

You can use the name of a Java interface as you would a data type when you declare a variable, a data field, or a method’s parameter or return type. Although using an interface name in one of these ways is often a good idea, you must ensure compatible data types when assigning values to variables or passing arguments to methods. To do so, you might have to cast a data type.

Details and examples

Let’s write an interface for objects that have an area, such as circles and squares. The interface can specify a get method for the area. This is shown below:

/** An interface for classes of objects that have an area.
@author Frank M. Carrano */
public interface Measurable
{
/** Gets the area of this object.
@return the area. */
public double getArea();
} // End Measurable

Any class that implements this interface must define the getArea method.

The following UML diagram illustrates two such classes, Square and Circle, and the client, ShapeDemo. Let’s assume that we have defined these three classes. The classes are depicted as follows:

Because Square implements Measurable, we can assign a Square object to a variable of type Measurable without a cast:

Measurable box = new Square(2.5);

However, to subsequently assign box to a variable of type Square it will require a cast:

Square boxAlias = (Square)box;

✏️ Note: Conversions between class types and interface types

  • Conversion from a class type to an interface type is implicit and legal.
  • Conversion from an interface type to a class type requires an explicit cast to the class type.

Again, consider the following statement:

Measurable box = new Square(2.5);

Since the Measurable interface declares the getArea method and the Square class implements getArea, we can invoke this method in a statement such as:

double boxArea = box.getArea(); // Legal

However, Square also defines the getSide method which Measurable does not declare. Thus, the following statement is illegal and would cause a syntax error:

double boxSide = box.getSide(); // ILLEGAL: getSide is not in Measurable

Because compilation occurs before execution, the compiler does not know that box will reference a Square object when this statement executes. But you do!

You can tell the compiler to assume that box will reference a Square object by casting box to Square. For example, you could write this:

Square boxAsSquare = (Square)box; // I promise that box references an object that has Square methods.

The compiler will believe you, so the following statement will compile and execute correctly, as seen here:

double boxAsSquareSide = boxAsSquare.getSide(); // Legal due to previous cast

If, however, box does not reference a Square object, an exception will occur if you try to cast it to Square. For example, if box actually references a Circle object instead of a Square object, you cannot cast box to Square.

The following program contains the previous statements so that you can verify their effect and experiment with them:

ShapesDemo.java
Circle.java
Square.java
Measurable.java
/** A class of circles.
@author F. M. Carrano */
public class Circle implements Measurable
{
private double radius;
public Circle(double newRadius)
{
radius = newRadius;
} // End constructor
public double getRadius()
{
return radius;
} // End getRadius
public double getArea()
{
return Math.PI * radius * radius;
} // End getArea
public String toString()
{
return "A circle whose radius is " + radius;
} // End toString
} // End Circle

Now, consider the following method that we could add to the previous program:

/** Displays the side of a Square object. */
public static void displaySide(Square aSquare, String units)
{
   double side = aSquare.getSide();
   System.out.println("The Square's side is " + side + " " + units + ".");
} // End displaySide

The data type of displaySide’s parameter, aSquare, is Square. You can pass it as an argument whose data type is Square, but not Measurable. And, as we have seen, you could not assign a Measurable type variable to one of the Square types. A cast to Square would be required in both cases.

Now , let’s define the following method whose parameter, shape, has Measurable as its data type:

/** Displays the area of a Measurable object. */
public static void displayArea(Measurable shape, String units)
{
   double area = shape.getArea();
   System.out.println("The Measurable shape's area is " + area + " square " + units + ".");
} // End displayArea

You can pass this method a variable whose data type is either Measurable, or a class, such as Square or Circle, that implements Measurable.

The following program is like the previous one, but it includes the previous two methods. Run the program and try various calls to the methods until you are comfortable with casting interface types. This is shown below:

ShapesDemo.java
Measurable.java
Circle.java
Square.java
public class ShapesDemo
{
public static void main(String[] args)
{
Square mySquare = new Square(2.5);
double mySquareSide = mySquare.getSide();
double mySquareArea = mySquare.getArea();
System.out.println("A Square whose side is " + mySquareSide + " inches has an area of " + mySquareArea + " square inches.");
Measurable box = new Square(3.0); // Implicit cast from class type to interface type
double boxArea = box.getArea(); // LEGAL: Measurable declares getArea
// Square's getArea is called
// double boxSide = box.getSide(); // ILLEGAL: getSide is not in Measurable
// The compiler does not know box references a Square object, but you do!
Square boxAsSquare = (Square)box; // I promise that box references an object that has Square methods. The compiler will believe you.
double boxAsSquareSide = boxAsSquare.getSide(); // Legal due to cast
double boxAsSquareArea = boxAsSquare.getArea(); // Legal due to cast
System.out.println("A Measurable box whose side is " + boxAsSquareSide + " inches has an area of " + boxAsSquareArea + " square inches.");
// If you break your promise, an exception will occur.
box = new Circle(3.0); // Legal: Circle is Meaurable
// boxAsSquare = (Square)box; // Illegal: box does not reference a Square
System.out.println("The Measurable box as a Circle object has an area of " + box.getArea() + " square inches.");
displaySide(mySquare, "inches"); // Pass a Square argument to a Square parameter
displayArea(mySquare, "inches"); // Pass a Square argument to a Measurable parameter
displayArea(boxAsSquare, "inches"); // Pass a Square argument to a Measurable parameter
displayArea(box, "inches"); // Pass a Measurable argument to a Measurable parameter
// displaySide(box, "inches"); // ILLEGAL: Pass a Measurable argument to a Square parameter
} // End main
/** Displays the side of a Square object. */
public static void displaySide(Square aSquare, String units)
{
double side = aSquare.getSide();
System.out.println("The Square's side is " + side + " " + units + ".");
} // End displaySide
/** Displays the area of a Measurable object. */
public static void displayArea(Measurable shape, String units)
{
double area = shape.getArea();
System.out.println("The Measurable shape's area is " + area + " square " + units + ".");
} // End displayArea
} // End ShapesDemo

Free Resources