122

Having being taught during my C++ days about evils of the C-style cast operator I was pleased at first to find that in Java 5 java.lang.Class had acquired a cast method.

I thought that finally we have an OO way of dealing with casting.

Turns out Class.cast is not the same as static_cast in C++. It is more like reinterpret_cast. It will not generate a compilation error where it is expected and instead will defer to runtime. Here is a simple test case to demonstrate different behaviors.

package test;

import static org.junit.Assert.assertTrue;

import org.junit.Test;


public class TestCast
{
    static final class Foo
    {
    }

    static class Bar
    {
    }

    static final class BarSubclass
        extends Bar
    {
    }

    @Test
    public void test ( )
    {
        final Foo foo = new Foo( );
        final Bar bar = new Bar( );
        final BarSubclass bar_subclass = new BarSubclass( );

        {
            final Bar bar_ref = bar;
        }

        {
            // Compilation error
            final Bar bar_ref = foo;
        }
        {
            // Compilation error
            final Bar bar_ref = (Bar) foo;
        }

        try
        {
            // !!! Compiles fine, runtime exception
            Bar.class.cast( foo );
        }
        catch ( final ClassCastException ex )
        {
            assertTrue( true );
        }

        {
            final Bar bar_ref = bar_subclass;
        }

        try
        {
            // Compiles fine, runtime exception, equivalent of C++ dynamic_cast
            final BarSubclass bar_subclass_ref = (BarSubclass) bar;
        }
        catch ( final ClassCastException ex )
        {
            assertTrue( true );
        }
    }
}

So, these are my questions.

  1. Should Class.cast() be banished to Generics land? There it has quite a few legitimate uses.
  2. Should compilers generate compile errors when Class.cast() is used and illegal conditions can be determined at compile time?
  3. Should Java provide a cast operator as a language construct similar to C++?
Alexander Pogrebnyak
  • 44,836
  • 10
  • 105
  • 121
  • 4
    Simple answer: (1) Where is "Generics land"? How is that different from how the cast operator is used now? (2) Probably. But in 99% of all Java code ever written, it is *extremely* unlikely for anyone to use `Class.cast()` when illegal conditions can be determined at compile time. In that case, everybody but you just uses the standard cast operator. (3) Java does have a cast operator as a language construct. It is not similar to C++. That is because many of Java's language constructs are not similar to C++. Despite the superficial similarities, Java and C++ are quite different. – Daniel Pryden Oct 12 '09 at 16:24
  • This tutorial explains the need for cast() method in generics, elegantly: https://www.baeldung.com/java-type-casting – Pam Stums Dec 09 '21 at 09:44

8 Answers8

137

I've only ever used Class.cast(Object) to avoid warnings in "generics land". I often see methods doing things like this:

@SuppressWarnings("unchecked")
<T> T doSomething() {
    Object o;
    // snip
    return (T) o;
}

It's often best to replace it by:

<T> T doSomething(Class<T> cls) {
    Object o;
    // snip
    return cls.cast(o);
}

That's the only use case for Class.cast(Object) I've ever come across.

Regarding compiler warnings: I suspect that Class.cast(Object) isn't special to the compiler. It could be optimized when used statically (i.e. Foo.class.cast(o) rather than cls.cast(o)) but I've never seen anybody using it - which makes the effort of building this optimization into the compiler somewhat worthless.

hexacyanide
  • 88,222
  • 31
  • 159
  • 162
sfussenegger
  • 35,575
  • 15
  • 95
  • 119
  • My last word is that if `Foo.class.cast(o)` would behave like `(Foo)` and `(Foo)` were prohibited it would lead to 2 outcomes: less casual casting ( nobody likes to type a lot ). Easier search for cast instances in the source code. With that I'll leave the Dream Land and banish the Class.cast to Generics Land :). – Alexander Pogrebnyak Oct 12 '09 at 16:31
  • 8
    I think the correct way in this case would be to do an instance of check first like this: if (cls.isInstance(o)){ return cls.cast(o); } Except if you're sure the type will be correct, of course. – Puce Jan 19 '12 at 08:57
  • I think that cast() works quite nicely in some factory type patterns. For example casting data from some XDR to some internal representation based on a .class definition. This works much like ObjectiveC casting – Daniel Voina Jun 19 '13 at 11:06
  • 1
    Why is second variant better? Isn't first variant more efficient because caller's code would do dynamic cast anyway? – user1944408 Mar 04 '14 at 03:39
  • 1
    @user1944408 as long as your are strictly speaking about performance it may be, although pretty much negligible. I wouldn't like the idea of getting ClassCastExceptions where there isn't an obvious case though. Additionally, the second one works better where the compiler can't infer T, e.g. list.add(this.doSomething()) vs. list.add(doSomething(String.class)) – sfussenegger Mar 04 '14 at 11:05
  • 2
    But you can still get ClassCastExceptions when you call cls.cast(o) while you are not getting any warnings in compile time. I prefer the first variant but I would use the second variant when I need for example to return null if I cannot do casting. In that case I would wrap cls.cast(o) in a try catch block. I also agree with you that this is also better when compiler can't infer T. Thanks for the reply. – user1944408 Mar 04 '14 at 11:26
  • 1
    I also use the second variant when I need Class cls to be dynamic, for example if I use reflection but in all other cases I prefer the first one. Well I guess that's just a personal taste. – user1944408 Mar 04 '14 at 11:30
  • how about the performance of Class.cast(object) compared to (T) object in a high QPS scenario? – lily Feb 22 '21 at 11:47
23

First, you are strongly discouraged to do almost any cast, so you should limit it as much as possible! You lose the benefits of Java's compile-time strongly-typed features.

In any case, Class.cast() should be used mainly when you retrieve the Class token via reflection. It's more idiomatic to write

MyObject myObject = (MyObject) object

rather than

MyObject myObject = MyObject.class.cast(object)

EDIT: Errors at compile time

Over all, Java performs cast checks at run time only. However, the compiler can issue an error if it can prove that such casts can never succeed (e.g. cast a class to another class that's not a supertype and cast a final class type to class/interface that's not in its type hierarchy). Here since Foo and Bar are classes that aren't in each other hierarchy, the cast can never succeed.

Beryllium
  • 12,808
  • 10
  • 56
  • 86
notnoop
  • 58,763
  • 21
  • 123
  • 144
  • I totally agree that casts should be used sparingly, that's why having something that can be easily searched would be of a great benefit for code refactoring/maintaining. But the problem is that although at a first glance `Class.cast` seem to fit the bill it creates more problems than it solves. – Alexander Pogrebnyak Oct 12 '09 at 16:01
17

It's always problematic and often misleading to try and translate constructs and concepts between languages. Casting is no exception. Particularly because Java is a dynamic language and C++ is somewhat different.

All casting in Java, no matter how you do it, is done at runtime. Type information is held at runtime. C++ is a bit more of a mix. You can cast a struct in C++ to another and it's merely a reinterpretation of the bytes that represent those structs. Java doesn't work that way.

Also generics in Java and C++ are vastly different. Don't concern yourself overly with how you do C++ things in Java. You need to learn how to do things the Java way.

cletus
  • 616,129
  • 168
  • 910
  • 942
  • Not all information is held at runtime. As you can see from my example `(Bar) foo` does generate an error at compile time, but `Bar.class.cast(foo)` does not. In my opinion if it is used in this manner it should. – Alexander Pogrebnyak Oct 12 '09 at 15:56
  • 5
    @Alexander Pogrebnyak: Don't do that then! `Bar.class.cast(foo)` explicitly tells the compiler that you want to do the cast at runtime. If you want a compile-time check on the validity of the cast, your only choice is to do the `(Bar) foo` style cast. – Daniel Pryden Oct 12 '09 at 16:17
  • What do you think is the way to do the same way? since java does not support multiple class inheritance. – Yamur Dec 08 '17 at 15:50
14

Class.cast() is rarely ever used in Java code. If it is used then usually with types that are only known at runtime (i.e. via their respective Class objects and by some type parameter). It is only really useful in code that uses generics (that's also the reason it wasn't introduced earlier).

It is not similar to reinterpret_cast, because it will not allow you to break the type system at runtime any more than a normal cast does (i.e. you can break generic type parameters, but can't break "real" types).

The evils of the C-style cast operator generally don't apply to Java. The Java code that looks like a C-style cast is most similar to a dynamic_cast<>() with a reference type in Java (remember: Java has runtime type information).

Generally comparing the C++ casting operators with Java casting is pretty hard since in Java you can only ever cast reference and no conversion ever happens to objects (only primitive values can be converted using this syntax).

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
6

Generally the cast operator is preferred to the Class#cast method as it's more concise and can be analyzed by the compiler to spit out blatant issues with the code.

Class#cast takes responsibility for type checking at run-time rather than during compilation.

There are certainly use-cases for Class#cast, particularly when it comes to reflective operations.

Since lambda's came to java I personally like using Class#cast with the collections/stream API if I'm working with abstract types, for example.

Dog findMyDog(String name, Breed breed) {
    return lostAnimals.stream()
                      .filter(Dog.class::isInstance)
                      .map(Dog.class::cast)
                      .filter(dog -> dog.getName().equalsIgnoreCase(name))
                      .filter(dog -> dog.getBreed() == breed)
                      .findFirst()
                      .orElse(null);
}
Caleb
  • 1,484
  • 2
  • 12
  • 10
3

C++ and Java are different languages.

The Java C-style cast operator is much more restricted than the C/C++ version. Effectively the Java cast is like the C++ dynamic_cast if the object you have cannot be cast to the new class you will get a run time (or if there is enough information in the code a compile time) exception. Thus the C++ idea of not using C type casts is not a good idea in Java

mmmmmm
  • 32,227
  • 27
  • 88
  • 117
0

In addition to remove ugly cast warnings as most mentioned ,Class.cast is run-time cast mostly used with generic casting ,due to generic info will be erased at run time and some how each generic will be considered Object , this leads to not to throw an early ClassCastException.

for example serviceLoder use this trick when creating the objects,check S p = service.cast(c.newInstance()); this will throw a class cast exception when S P =(S) c.newInstance(); won't and may show a warning 'Type safety: Unchecked cast from Object to S'.(same as Object P =(Object) c.newInstance();)

-simply it checks that the casted object is instance of casting class then it will use the cast operator to cast and hide the warning by suppressing it.

java implementation for dynamic cast:

@SuppressWarnings("unchecked")
public T cast(Object obj) {
    if (obj != null && !isInstance(obj))
        throw new ClassCastException(cannotCastMsg(obj));
    return (T) obj;
}




    private S nextService() {
        if (!hasNextService())
            throw new NoSuchElementException();
        String cn = nextName;
        nextName = null;
        Class<?> c = null;
        try {
            c = Class.forName(cn, false, loader);
        } catch (ClassNotFoundException x) {
            fail(service,
                 "Provider " + cn + " not found");
        }
        if (!service.isAssignableFrom(c)) {
            fail(service,
                 "Provider " + cn  + " not a subtype");
        }
        try {
            S p = service.cast(c.newInstance());
            providers.put(cn, p);
            return p;
        } catch (Throwable x) {
            fail(service,
                 "Provider " + cn + " could not be instantiated",
                 x);
        }
        throw new Error();          // This cannot happen
    }
0

Personally, I've used this before to build a JSON to POJO converter. In the case that the JSONObject processed with the function contains an array or nested JSONObjects (implying that the data here isn't of a primitive type or String), I attempt to invoke the setter method using class.cast() in this fashion:

public static Object convertResponse(Class<?> clazz, JSONObject readResultObject) {
    ...
    for(Method m : clazz.getMethods()) {
        if(!m.isAnnotationPresent(convertResultIgnore.class) && 
            m.getName().toLowerCase().startsWith("set")) {
        ...
        m.invoke(returnObject,  m.getParameters()[0].getClass().cast(convertResponse(m.getParameters()[0].getType(), readResultObject.getJSONObject(key))));
    }
    ...
}

Not sure if this is extremely helpful, but as said here before, reflection is one of the very few legitimate use case of class.cast() I can think of, at least you have another example now.

Wep0n
  • 392
  • 3
  • 15