Yeah, it’s actually pretty much the standard with more functional programming. Sadly, I haven’t had the chance to try it out much in Java (only in certain areas) since I wouldn’t actually try to rewrite/wrap all of Java’s standard APIs and libraries that use Exceptions.
But the place where I have tried it are streams, JavaScripts promises and of course the few “functional” languages that I’ve tried.
If you want to see it in action, you could just look at any Rust program as that’s the only way that Rust allows you to handle errors short of completely halting the program.
How it works is described here: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html
In the case of rust it’s combined with discriminated unions (or just enums in rust). That has advantages and disadvantages of course, but the idea stays the same: the returned type encompasses both the successful as well as the exceptional case.