JVM Advent

The JVM Programming Advent Calendar

Dynamic casting in Java

I’m a big fan of Baeldung’s blog. One of his previous post is about casting in Java. Unfortunately, I feel it misses one important point: dynamic casting. Since that’s is (very) relatively new, this post will try to fill that gap.

Do not use casting

The first thing is that it should be relatively easy to avoid casting.

Using polymorphism

Polymorphism is a great way not to cast. Consider the following code:

List animals = new ArrayList();
animals.add(new Cat());
animals.add(new Dog());
for (Animal animal: animals) {
    if (animal instanceof Cat) {
        ((Cat) animal).mew();
    } else if (animal instanceof Dog) {
        ((Dog) animal).bark();
    } else if (animal instanceof Snake) {
        ((Snake) animal).hiss();
    }
}

If all object types in the collection inherit from a single type, it’s possible to use polymorphism. Just add one method in this single type and override it in the subtypes.

public abstract class Animal {
    public abstract void makeSound();
}

public class Dog {
    @Override public void makeSound() {
        bark();
    }
}

public class Cat {
    @Override public void makeSound() {
        mew();
    }
}

Using generics

Polymorphism cannot be always applied. For example, this is the case when:

  • Items are not instances of related type
  • The parent type is not within our control sphere
  • etc.

In those cases, generics is another way to avoid casting.

Before Java 5, the following code would be the norm:

List dates = new ArrayList();
dates.add(new Date());
Object object = dates.get(0);
Date date = (Date) object;

On line 4, casting required. Though the runtime type is Date, the compiler has no way to know about it.

With generics, the above code can be rewritten:

List<Date> dates = new ArrayList<>();
dates.add(new Date());
Date date = dates.get(0);

On line 3, no casting is required: the compiler has enough information thanks to generics.

When casting is mandatory

Despite to what some fanatics think, it happens that sometimes casting cannot be ignored.

One such use-case is the Servlet API. Map storing objects in servlet context/request/session do not use generics. And even if they did, they would be using Object.

// In a servlet
ServletContext context = getServletContext();
context.put("date", new Date());

// Somewhere else
ServletContext context = getServletContext();
Object object = context.get("date");
Date date = (Date) object;

Dynamic casting

The only form of casting originally available was static casting. Which means the casting type needs to be known at compile time. For example, let’s imagine a method that accepts a Stream<Object>, filters all element of a certain type and returns those elements in the right type. This is an example of the usage:

List<?> items = ...
List dates = filter(Date.class, items);

There’s no way to implement the filter method with static casting. There are actually two issues:

  1. The instanceof operator requires a type, not a Class instance e.g. item instanceof Date
  2. The cast syntax as well e.g. (Date) item

The instanceof operator can be replaced with a call to Class.isInstance(Object)(since JDK 1.1). This is quite well-known, if not widely used.

The API to replace the cast syntax, however, is much more “recent”. There’s a Class.cast(Object) method since JDK 1.5 It is a simple wrapper around the legacy syntax.

Using both methods, it’s finally possible to implement the filter method above.

static  List filter(Class clazz, List<?> items) {
    return items.stream()
        .filter(clazz::isInstance)
        .map(clazz::cast)
        .collect(Collectors.toList());
}

Using the casting API allows dynamic casting. Without it, it’s not possible to implement the above method.

Conclusion

Despite what many people outside the ecosystem think, Java evolves, even if not very fast. However, developers needs to be acquainted with the new capabilities offered by each version.

To go further:

Author: Nicolas Fränkel

Nicolas Fränkel is a Developer Advocate with 15+ years experience consulting for many different customers, in a wide range of contexts (such as telecoms, banking, insurances, large retail and public sector). Usually working on Java/Java EE and Spring technologies, but with focused interests like Rich Internet Applications, Testing, CI/CD and DevOps. Also double as a teacher in universities and higher education schools, a trainer and triples as a book author.

You can find Nicolas’ weekly post on his blog.

Next Post

Previous Post

Leave a Reply

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

© 2024 JVM Advent | Powered by steinhauer.software Logosteinhauer.software

Theme by Anders Norén