Java is strongly typed language and it only allows casting an object to one of it parent classes or interfaces. That is, if you have the following:
class A {}
interface I {}
class B extends A implements I {}
class C {}
You can cast an object of type B like so:
B b = new B();
A a = b; // no need for explicit casting
I i = b;
Object o = b; // Object is implicit parent of A
C c = b; // ERROR C isn't a parent class of b
This is called upcasting. You can also downcast:
A a = new B();
B b = (B) b;
you need to use explicit cast here and JVM will check in runtime if the cast is really allowed.
The String.class.cast(o)
casting is useful, when you don't know the specific type you're casting to in compile time.
The .toString()
isn't casting. It's just a method, which is supposed to return a String representation of your object. But the representation isn't the object, it's just a representation. This method is defined in the Object
class, so it's present in all classes and is baked in the language somewhat. That is, if you write
String s = "Hell " + o;
JVM will call o.toString()
method for you to obtain it representation.