at the time of compilation compiler thinks X as an Object type because <X extends Object> . So an object of Object class can be casted to any of its subtypes, which is Double, because they come in same hierarchy in the inheritance tree.
Casting is effectively telling the compiler "Trust me. I know you think that's an X, which could be anything, but I know it's going to be a Double, so treat it like a double". You're overriding the compiler, so the compiler lets you do it and relies on the runtime to catch any errors.
The compiler will only stop you if it knows that you're wrong - if the cast cannot possibly work. As an example, try changing your first line to X extends Integer.