3

Say you have a clean class like this:

public class A {
    // Stuff
}

And a interface like this:

public interface G {
    // Stuff
}

Why am I allowed to do this:

A a = new A();
((G) a) // No errors thrown

I can't understand why it should be possible to cast from the class A to the interface G when they have nothing to do with each other. Can someone please explain this to me?


Follow-up. If I make the following:

public class C implements G {
    // Stuff
}

This won't compile:

((C) a)

What is the difference between a class that implements a interface and just the interface?

EDIT: I get a compiler-error saying:

Cannot cast from A to C

Basilevs
  • 22,440
  • 15
  • 57
  • 102
OptimusCrime
  • 14,662
  • 13
  • 58
  • 96
  • 1
    `No errors thrown` you mean at runtime or compile time? – Kent May 02 '13 at 20:38
  • Look at this discussion http://stackoverflow.com/questions/6056124/do-interfaces-inherit-from-object-class-in-java – Prayag May 02 '13 at 20:42
  • Note that the compiler doesn't *crash*; it's just telling you about a compilation error. – dlev May 02 '13 at 20:53
  • @dlev: Ah, thanks for pointing out, I am not so familiar with the technicals terms here. This is for my exam in OOP. I am doing earlier exams to prepare and this was something I did not understand. – OptimusCrime May 02 '13 at 20:54

6 Answers6

7

Casting means you know better than the compiler what's valid. You are telling the compiler to shut up and follow your lead. The compiler can tell in a few cases that a cast is invalid, but it is easy to fool.

Most casts tend to be from Object to something else, such as in getting an object out of a non-genericized collection or when getting a remote object using PortableRemoteObject.narrow. These casts always compile because whatever you cast to (as long as it's an Object, and not a primitive) is always a valid subclass of Object.

There are some rules for casting in the Java language specification in the section Reference Type casting (5.5.1). If the compiler can figure out there is no relationship between the classes (the compiler can tell the classes are different and neither is a subclass of the other) then it will reject the cast.

The added example is interesting, it fails because the compiler has enough information to tell that the cast is invalid. If you change the code to:

    A a = new A();
    G g = (G)a;
    Object o = a;
    C c = (C)o;

then it compiles fine again (even though it is just as erroneous).

Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
  • Makes sense, but it won't compile if I make `Class C implements G`, and cast from `A` to `C`. I just don't see the difference. – OptimusCrime May 02 '13 at 20:42
  • @OptimusCrime: could you flesh out that example and add it to the question? – Nathan Hughes May 02 '13 at 20:45
  • Done ^. I really don't see the difference. – OptimusCrime May 02 '13 at 20:46
  • 3
    +1. Quoting a friend of mine: `ClassCastException means the compiler knew better than you, tricked you, and you fell for it.` – Viccari May 02 '13 at 20:56
  • Do you know just how this "enough information" works? I know the outcome of this exact situation now, but this was a question on an exam in my course last year. I'd like to be as prepared as possible. – OptimusCrime May 02 '13 at 21:07
  • @OptimusCrime: The compiler is looking for compile-time type information that tells it the classes are unrelated. see the link to the jls, there's an example there (Example 5.5.1-1). – Nathan Hughes May 02 '13 at 21:15
2

This is allowed at compile time, because the Java compiler lets you do this; it assumes you know what you're doing.

However, the JVM figures it out and throws a ClassCastException at runtime.

rgettman
  • 176,041
  • 30
  • 275
  • 357
  • Thanks for great answer. I see that I left out a part of my question. Could you take a look at the follow-up-question and describe the difference? – OptimusCrime May 02 '13 at 20:47
2

The issue here is that because G is an interface, it is possible that there is a subclass of A that implements G. Therefore it's at least possible that at runtime the type of the object stored in a implements G.

"But I just assigned a to contain type A! Stupid compiler!" It's important to remember that when assessing the potential success of a cast, the compiler looks only at the compile-time type of the variable. As such, the compiler doesn't try to figure out what will actually be in the variable, it just looks at the type of the variable itself.

Note that if you declare A to be final, then this becomes a compile-time error, since now the compiler knows there are no subclasses of A, and A itself doesn't implement G, and so the cast can never succeed at runtime.

Finally, the same logic applies to casting C to A: C doesn't derive from A, and the compiler knows that no subclass of C derives from A (since they all derive from C at some point!) and so there's no possibility that the cast could succeed at runtime.

dlev
  • 48,024
  • 5
  • 125
  • 132
  • Ah, I think I get it. The difference is that when casting to interface, the compiler does not check if it's valid because it COULD be, but when casting to implementation of the interface, it actually does the check? Because `C` is a "normal" class? – OptimusCrime May 02 '13 at 20:59
  • @OptimusCrime Yeah. In essence, the compiler will check to see if it has enough information to determine that the cast is impossible. If it doesn't, then it allows the compilation, and lets the JVM sort out the actual legalit of the cast at runtime. If it can determine that the cast could never succeed, then it will generate a compiler error. – dlev May 02 '13 at 21:06
  • I think I got it now. Still think this is really weird. Thank you a lot for the effort! – OptimusCrime May 02 '13 at 21:24
1

Sure, but this is also valid:

public class A {}
public class B {}

A a = new A();
((B) a);

You can cast almost anything to anything, casting is for when you know what type you want.

(However when you use some casts, such as the example above, you'll get a ClassCastException at runtime when Java realizes that it won't cast right.)

tckmn
  • 57,719
  • 27
  • 114
  • 156
1

The compiler doesn't necessarily know whether or not a implements G or not.

Casting pretty much tells the compiler to take this variable, and treat it like a different type.

If you try to do an operation on the cast variable as though it implemented G(when it doesn't) you'll get a runtime error

1

Casting correctness can only be checked at runtime. So in your example, even though it might be obvious to you, for the program it won't know it's incorrect until it's actually running and attempts to cast the variable a as the interface G. At this point it will throw a ClassCastException.

greedybuddha
  • 7,488
  • 3
  • 36
  • 50