views:

2094

answers:

5

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 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 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 operators as a language construct similar to C++?
+6  A: 

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
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
@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
+3  A: 

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 compile-time strongly-type features.

In any case, Cast.cast() should be used mainly when you retrieve the class 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 that cast checks at compile 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.

notnoop
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
+3  A: 

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 the 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
`dynamic_cast<>()` with a reference type.
Tom Hawtin - tackline
@Tom: is that edit correct? My C++ is very rusty, I had to re-google a lot of it ;-)
Joachim Sauer
+1 for: "The evils of the C-style cast operator generally don't apply to Java." Quite true. I was just about to post those exact words as a comment on the question.
Daniel Pryden
+1  A: 

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

Mark
+3  A: 

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 usecase 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.

sfussenegger
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