Core Java

Java Type Casting

This example will dive into the core concepts of Java Type casting. Java enables type casting of the primitive data types as well as objects.

1. Introduction

One of the fundamental aspects of Object Oriented Programming is the ability to be able to juggle between different objects and data types. The Java compiler requires all variables in a program to be assigned a certain data type before it can be used. Variables can be assigned one of the eight primitive data types, or they will need to be declared to be an object. This requirement by the Java compiler is tied to the fact that each data type is allocated a certain amount of memory.

In this example, we will explain the theory behind casting and its various types. Through coding examples, we will be able to demonstrate how we can use other object oriented concepts such as Inheritance and Polymorphism. Finally, we will demonstrate specific keywords and methods made available by Java to verify an objects type as well as to implement casting.

2. Technologies Used

The example code used in this article was written and run using

  1. Java 11.0.5
  2. Apache Maven 3.6.0
  3. Junit 4.13
  4. Intellij IDEA 2020.1(EDU)

3. Maven project

In this step I will create a project that has type casting examples.

3.1 Dependencies

I will include Junit in the pom.xml

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>jcg.ssowmya.demo</groupId>
    <artifactId>typeCasting</artifactId>
    <version>1.0-SNAPSHOT</version>
    <build>
        <sourceDirectory>src</sourceDirectory>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <release>11</release>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
        </dependency>
    </dependencies>
</project>

2. What is casting?

Casting simply refers to the technique used to convert one data type to another. While casting a variable from one type to another, we need to be cognizant of the underlying memory related considerations made by the Java compiler and the Java Virtual Machine(JVM).

To give a real world example of the need for casting, imagine an inventory form that asks the user to input the price of an item. We may accept the input as a text, but we will surely want to perform some validation and display this back as a decimal value. Java allows us to be able to do this through type casting.

3. Primitive Casting

Primitive casting is the type of casting that happens within the primitive data types. As mentioned earlier, Java has eight primitive data types: boolean, byte, char, short, int, long, float and double. These types differ in their size and range of values they can store. There are 2 types of primitive casting and I will illustrate that with code as follows.

3.1 PrimitiveTypeCastTest

In this example, I will create a Junit class named PrimitiveTypeCastTest to demonstrate the different types of Primitive casting.

PrimitiveTypeCastTest.java

package jcg.ssowmya.demo.typecast;


import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;

public class PrimitiveTypeCastTest {
    @Test
    public void testImplicitShortToIntCasting() {
        short shortVar = 45;
        int castVar= shortVar;
        System.out.println("Short val : "+shortVar);
        System.out.println("Int val : "+castVar);
        assertEquals(shortVar,castVar);
    }

    @Test
    public void testImplicitIntToFloatCasting() {
        int intVar = Integer.MIN_VALUE;
        float floatVar= intVar;
        System.out.println("Int val : "+intVar);
        System.out.println("Float val : "+floatVar);
        assertEquals(intVar,floatVar,0);
    }
    @Test
    public void testCharToShortCasting() {
        char unsignedCharVar= 'a';
        System.out.println("Char val : "+unsignedCharVar);
        short signedShortVar = (short)unsignedCharVar;
        System.out.println("Short val : "+signedShortVar);
        assertEquals(unsignedCharVar,signedShortVar);

    }

    @Test
    public void testSignedShortToCharCasting() {

        short signedShortVar = -97;
        System.out.println("Short val : "+signedShortVar);
        char unsignedCharVar= (char)signedShortVar;
        System.out.println("Char val : "+unsignedCharVar);
        assertNotEquals(signedShortVar,unsignedCharVar);
    }

    @Test
    public void testUnSignedShortToCharCasting() {

        short unsignedShortVar = 97;
        System.out.println("Short val : "+unsignedShortVar);
        char unsignedCharVar= (char)unsignedShortVar;
        System.out.println("Char val : "+unsignedCharVar);
        assertEquals(unsignedCharVar,unsignedShortVar);
    }
    @Test
    public void testExplicitFloatToIntCasting() {
        float floatVar = Float.MAX_VALUE;
        int intVar= (int)floatVar;
        System.out.println("Float val : "+floatVar);
        System.out.println("Int val : "+intVar);
        assertNotEquals(intVar,floatVar,0);
    }
}

3.2 Implicit Casting

For this type of casting, no operators, keywords or methods are required to do the conversion. With the exception of short/char, all primitive data types that are either smaller in size or in precision will be automatically converted to the higher type.

In the testImplicitShortToIntCasting method above, we notice that the Java compiler has implicity converted shortVar to the int variable castVar. We can also assert that both these variables have the same value. Similarly, in testImplicitIntToFloatCasting, we can see that there is no need of any additional keywords to convert the variable intVar to floatVar. Though the implicitly casted variables are equal in value, they are displayed differently based on their data type. Line 23 in the code snippet displays as Int val : -2147483648, while line 24 displays as Float val : -2.14748365E9

3.3 Explicit Casting

 In Java, variables that need to be explicitly cast require the mention of the data type to be converted to. The conversion between char and short is always explicit. In the testCharToShortCasting method above, we notice the usage of (short) keyword in line 32 while converting between unsignedCharVar and signedShortVar. Also, as expected, unsignedCharVar displays as a, with the value of signedShortVar printed as 97.

Explicit casting from a float to an int type could cause a loss of precision. As we see from the testExplicitFloatToIntCasting() method, since an int has a much lesser maximum value than a float, the final value of intVar is different from that of floatVar.

For a better understanding of how implicit and explicit casting work with primitive types, you can see the output of running mvn -Dtest=PrimitiveTypeCastTest test from the command line.

Output of PrimitiveTypeCastTest.java

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running jcg.ssowmya.demo.typecast.PrimitiveTypeCastTest
Char val : a
Short val : 97
Short val : 97
Char val : a
Short val : 45
Int val : 45
Short val : -97
Char val : ゚
Float val : 3.4028235E38
Int val : 2147483647
Int val : -2147483648
Float val : -2.14748365E9
Tests run: 6, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.272 sec

Results :

Tests run: 6, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  9.810 s
[INFO] Finished at: 2020-05-20T05:46:53-04:00
[INFO] ------------------------------------------------------------------------

4. Object Casting

Object casting refers to type conversion within objects. As we know, objects are placeholders or references, so when an object is cast to another type, its data type gets converted just like primitive casting. This will in turn alter the methods that can be accessed by the converted object, as well the object’s property values. Object casting also needs to adhere to the principles of inheritance, where casting only takes place between super and sub-classes.

4.1 UniversityUser Class

In this example, I will create a base class UniversityUser. This will serve as the parent class to 2 other classes Student and Staff.

UniversityUser.java

package main.java.jcg.ssowmya.demo.typecast;

public class UniversityUser {

    private String universityId;
    private String name;
    private String affiliation;
    private String email;

    public UniversityUser(String universityId, String name, String affiliation, String email) {
        this.universityId = universityId;
        this.name = name;
        this.affiliation = affiliation;
        this.email = email;
    }

    public String getUniversityId() {
        return universityId;
    }

    public void setUniversityId(String universityId) {
        this.universityId = universityId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAffiliation() {
        return affiliation;
    }

    public void setAffiliation(String affiliation) {
        this.affiliation = affiliation;
    }
    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
    public boolean getLibraryAccess() {
        return true;
    }
}

4.2 Student class

In this example , I will create the Student class that extends the UniversityUser class.

Student.java

package main.java.jcg.ssowmya.demo.typecast;

public class Student extends UniversityUser{
    private boolean enrollmentStatus;
    private String college;
    private String classification;

    public Student(String universityId, String name, String affiliation, String email, boolean enrollmentStatus, String college, String classification) {
        super(universityId, name, affiliation, email);
        this.enrollmentStatus = enrollmentStatus;
        this.college = college;
        this.classification = classification;
    }

    public boolean isEnrollmentStatus() {
        return enrollmentStatus;
    }

    public void setEnrollmentStatus(boolean enrollmentStatus) {
        this.enrollmentStatus = enrollmentStatus;
    }

    public String getCollege() {
        return college;
    }

    public void setCollege(String college) {
        this.college = college;
    }

    public String getClassification() {
        return classification;
    }

    public void setClassification(String classification) {
        this.classification = classification;
    }
    public boolean validateStudent() {
        boolean isValidStudent = false;
        boolean isValidUniversityUser = new ValidateUniversityUser().validateUniversityId(this);
        if(!isValidUniversityUser)
            return isValidUniversityUser;
        else {
            //Perform student specific validations
            if(classification!=null && !"".equals(classification))
            {
                switch(classification) {
                    case "FR":
                    case "SO":
                    case "JR":
                    case "SR":
                        isValidStudent = true;
                        break;

                    default:
                        break;
                }
            }
        }
        return isValidUniversityUser && isValidStudent;
    }
    public boolean getLibraryAccess() {
        return validateStudent() && enrollmentStatus;
    }
}

4.3 Staff class

In this example, I will create the Staff class that also extends the UniversityUser class.

Staff.java

package main.java.jcg.ssowmya.demo.typecast;

public class Staff extends UniversityUser{
    private String employeeType;
    private String department;
    private String phone;

    public Staff(String universityId, String name, String affiliation, String email, String employeeType, String department, String phone) {
        super(universityId, name, affiliation, email);
        this.employeeType = employeeType;
        this.department = department;
        this.phone = phone;
    }

    public String getEmployeeType() {
        return employeeType;
    }

    public void setEmployeeType(String employeeType) {
        this.employeeType = employeeType;
    }

    public String getDepartment() {
        return department;
    }

    public void setDepartment(String department) {
        this.department = department;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }
    public boolean validateEmployee() {
        boolean isValidEmployee = false;
        boolean isValidUniversityUser = new ValidateUniversityUser().validateUniversityId(this);
        if(employeeType!=null && !"".equals(employeeType)) {
            switch(employeeType) {
                case "F/T":
                case "P/T":
                case "Hourly":
                    isValidEmployee = true;
                    break;
                default: break;
            }
        }
        return isValidUniversityUser && isValidEmployee;
    }
    public boolean getLibraryAccess() {
        return validateEmployee() && "F/T".equals(employeeType);
    }
}

4.4 ValidateUniversityUser class

In this example, I will create a ValidateUniversityUser class to perform validations on a UniversityUser object.

ValidateUniversityUser.java

package main.java.jcg.ssowmya.demo.typecast;

public class ValidateUniversityUser {

    public boolean validateUniversityId(UniversityUser universityUser) {
        boolean valid = false;
        if(universityUser.getUniversityId()==null || "".equals(universityUser.getUniversityId()))
            return valid;
        if(!universityUser.getUniversityId().startsWith("UID"))
            return valid;
        //Perform additional validations with a database
        valid = true;
        return valid;
    }
}

4.5 Upcasting

Upcasting refers to implicit casting within objects, where an object of the child(sub) class is converted to the parent(super) class. Similar to implicit casting in primitive types, no keywords or methods are required for upcasting.

4.6 ObjectTypeCastTest

In this example, I will create a Junit class ObjectTypeCastTest to show the types of object casting.

ObjectTypeCastTest.java

package test.java.jcg.ssowmya.demo.typecast;

import main.java.jcg.ssowmya.demo.typecast.Staff;
import main.java.jcg.ssowmya.demo.typecast.Student;
import main.java.jcg.ssowmya.demo.typecast.UniversityUser;
import org.junit.Test;

import static org.junit.Assert.*;

public class ObjectTypeCastTest {
    @Test
    public void testUpcastingStudentToUniversityUser() {
        UniversityUser universityUser = new UniversityUser("1234","Test UnivUser","Part time student","testunivuser@jcg.com");
        Student student = new Student("12345",
                "Test Student",
                "Student",
                "teststudent@jcg.com",true,
                "BE",
                "Freshman");
        universityUser = student;
        assertEquals("12345",universityUser.getUniversityId());
        //Compiler Error: universityUser.getCollege();
    }
    @Test
    public void testDowncastingUniversityUserToStaff() {
        UniversityUser universityUser  = new Staff("123456",
                "Test Staff",
                "Staff",
                "teststaff@jcg.com",
                "Full time",
                "Information Technologies",
                "123-456-7899");
        Staff staff = null;
        if(universityUser instanceof Staff)
            staff = (Staff)universityUser;
        assertEquals("123456", staff.getUniversityId());
        assertEquals("Full time", staff.getEmployeeType());
    }
    @Test
    public void testClassCast1UniversityUserToStaff() {
        UniversityUser universityUser  = new Student("12345",
                "Test Student",
                "Student",
                "teststudent@jcg.com",true,
                "BE",
                "Freshman");
        assertThrows(ClassCastException.class, ()->{Staff staff = (Staff)universityUser;});
    }
    @Test
    public void testClassCast2UniversityUserToStaff() {
        UniversityUser universityUser = new UniversityUser("1234","Test UnivUser","Part time student","testunivuser@jcg.com");
        assertThrows(ClassCastException.class,() -> {
            Staff staff = (Staff)universityUser;});
    }
    @Test
    public void testOverride() {
        UniversityUser universityUser1  = new Student("12345",
                "Test Student",
                "Student",
                "teststudent@jcg.com",false,
                "BE",
                "Freshman");
        UniversityUser universityUser2  = new Staff("UID123456",
                "Test Staff",
                "Staff",
                "teststaff@jcg.com",
                "F/T",
                "Information Technologies",
                "123-456-7899");
        assertFalse(universityUser1.getLibraryAccess());
        assertTrue(universityUser2.getLibraryAccess());
    }
    @Test
    public void testCastMethod() {
        UniversityUser universityUser  = new Student("UID12345",
                "Test Student",
                "Student",
                "teststudent@jcg.com",false,
                "BE",
                "Sophomore");
        Staff staff = null;
        Student student = null;
        if(universityUser instanceof Staff)
            staff = Staff.class.cast(universityUser);
        else if(universityUser instanceof Student)
            student = Student.class.cast(universityUser);
        assertNull(staff);
        assertNotNull(student);
    }
}

In the testUpcastingStudentToUniversityUser method, the student object is upcasted to an object of the parent class universityUser. This conversion modifies the property values of universityUser as can be asserted by line 20. However, this assignment does not allow the universityUser object to access methods of the Student class, such as getCollege. This will result in a compile time error.

4.7 Downcasting

Converting to an object of the child or sub-class is downcasting. Since the parent class can have many implementations or sub-classes, it is always recommended that we verify the type of the object before doing the conversion. Without this verification, a ClassCastException could be thrown.

4.7.1 Instanceof operator

As implied by the name, the instanceof operator will verify if an object is an instance of a particular class. Usage of this operator results in a boolean output. In the above coding example, the UniversityUser class has 2 sub-classes Student and Staff. In the ObjectTypeCastTest.java testDowncastingUniversityUserToStaff method, while downcasting from a universityUser to a staff object, we are ensuring that the underlying object is indeed of the class type Staff. Even though downcasting can be done without the usage of the instanceof operator, it is recommended for code clarity, as well as to avoid running into a ClassCastException.

4.7.2 ClassCastException

A ClassCastException is a runtime exception that is thrown by Java when objects are erroneously cast to another type. It is quite common to encounter a ClassCastException while downcasting, since this is not caught at compile-time and the object’s underlying data type may be declared elsewhere in the code.

In the testClassCast1UniversityUserToStaff method in ObjectTypeCastTest.java, the variable universityUser is of type Student. Hence in line 47, when we try to convert this to a Staff type, a ClassCastException is thrown because Student and Staff are unique sub-classes of UniversityUser, and cannot be converted to each other.

Another example of ClassCastException related to downcasting is shown in the testClassCast2UniversityUserToStaff method of ObjectTypeCastTest.java. Here we can see that since universityUser is originally an object of the parent class UniversityUser, it cannot be downcasted to a specific sub-class such as Staff.

5. Casting and Polymorphism

One of the core concepts of Object Oriented Programming is Polymorphism. Polymorphism refers to the ability of an object to take many forms. Java subtly ensures this since all objects can be cast to an Object. In the example above, we have seen how casting has made it possible for a Student object to take on 2 forms: one of type Student and another of type UniversityUser.

Casting and Polymorphism allow for code re-usability. Since sub-classes inherit from the parent class, they will share certain behaviors. In the ValidateUniversityUser class, the validateUniversityId method takes in a UniversityUser object to perform validation on the universityId property. Since all objects of type Staff and Student will need to pass through the same validation, this code can be reused. So we see that the validateStudent() method in line 40 of the Student class uses the this operator to invoke the method from the parent class UniversityUSer. A similar example of polymorphism is found in the Staff class in the validateEmployee() method.

6. Casting and Overriding

Overriding is the feature where a sub-class may want to implement its unique behaviors, that may be different from its parent class. The parent class UniversityUser has a method getLibraryAccess() that returns a true value by default. However, its sub-classes have implemented this method using custom logic. We can assert by looking at the testOverride() method of the ObjectTypeCastTest.java class that the specific implementations of the sub-classes are being invoked. In line 70, the object universityUser1 returns false for getLibraryAccess() since the enrollmentStatus is false.

7. cast() Method

Java allows all objects to use the cast() method to perform explicit casting. This is an alternate way of performing explicit casting. In the testCastMethod() of ObjectTypeCastTest.java, we see how the universityUser object has been successfully cast to a Student object. According to the Java API, this method will throw a ClassCastException if the object is not null and not assignable to another class type.

8. Summary

In this example, we have seen what casting is, and the different types of casting available in Java. We have also seen the concepts of Inheritance, Polymorphism, and Overriding within the context of Java Type Casting.

9. Download the Source Code

Download
You can download the full source code of this example here: Java Type Casting

Sowmya Shankar

Sowmya works as a Senior Web Applications Programmer in the higher educational sector, where she leads projects based on Java. She graduated from Anna University's Computer Sciences department, followed by a Masters degree in Computer and Information Sciences from the University of Delaware. Her project portfolio features diverse web applications using frameworks such as Struts2, Spring, Spring Boot, as well as her recent experiments in the world of Microservices and the React JS framework.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button