Say you have a clean class like this:
public class A {
// Stuff
}
And a interface like this:
public interface G {
/
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).