It's the correct design only if we assume that the design space didn't allow for a different return type. Kotlin for example offers toIntOrNull (https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/to-...) as an alternative.
To your first point: Another example in the design space would be Rust which works very similarly to Kotlin but returns more information in the failure case.
How does that fare with unnecessary boxing of primitives?