The problem in a large codebase is keeping consistency when upgrading.
I upgraded a codebase of approx 100 kloc from Java 7 to Java 8 a couple of years ago. As I didn't want mixed patterns for the same thing throughout the code base I replaced most of the loops with their streaming equivalent. I had the luxury of having the budget for doing so.
Mixing patterns of newer language features alongside older ones can make the code base hard to read.
There's something else amiss here. Compared to other platforms, upgrading Java, even on complex codebases, has never been a nightmare for me.