This _can_ be a problem with Java, admittedly. Sometimes you've got a single meaningful implementation, but you need an interface either for using it with services that implement such interface at runtime to create proxies (e.g. Spring, Hibernate, others) that augment its functionality, or to properly mock a dependency. BUT: the IDE should be your friend. Almost every Java IDE supports "go to the implementation".
And, what is the alternative? Fewer interfaces sometimes lead to even worse patterns (for testing: monkeypatching and concrete class subclassing). I prefer the clarity of declaring an interface wherever an interface makes sense.
Using a lot of interfaces and defining APIs (which are, mostly, collections of related interfaces) makes the Java ecosystem highly pluggable. I can mix-and-match a lot of different libraries and/or implementations, as long as there's an interface they can work together in a rather good fashion.
Consider Python: there're very few (and sometimes poorly defined) interfaces and API; so, quite often you need to pick a library/framework which implements everything and works poorly with other implementations.
But are you writing a library? If not, your product does not have to be plugable. Also if you end with multiple interfaces with only one implementation you should start with classes and extract interfaces when needed.
And I think the Java abstraction root problem comes from the way of doing "unit-testing" at the class level. Your users don't care about your application architecture: they want functionalities. Method and class level unit testing is just fossilizing your code and giving you some kind of feel-good number. You should be able to drop all your code, start anew with a new stack and have your tests still usable. Refactoring should never mean having to rewrite tests.
I'm not writing a libraries, but other people are. In Java I can pick my IoC container, my ORM, my web stack, as I wish.
I can hook-in web-related functionalities through the servlet API filters.
If I pick Django in Python I get a builtin ORM and web stack. Everything is within Django, for Django; extensions are Django-specific.
In order for that to work, you'll need your interfaces to be stable. It's impossible otherwise :-)
You should not test code. You should test functionalities.
You should be able to rewrite all your app in a new language and still be confident your tests will tell you if something broke. "Unit testing" at the class level won't do that.
You're talking about integration tests and/or end-to-end tests. I write those. Usually at the façade level, not at the network endpoint (which usually means the controller level, a shallow layer that often includes some ACL and serialization) but it's just a matter of taste.
> You should be able to rewrite all your app in a new language and still be confident your tests will tell you if something broke. "Unit testing" at the class level won't do that.
Unit tests, integration tests and e2e tests are different things that solve different concerns. An e2e test will rarely expose an obscure bug within an implementation, and a unit test will rarely expose concerns in the global application flow.
Both have their place. And they have nothing to do with the "interface vs not interface", IMHO.
I don't remember how it is done exactly (it's in my muscle memory), but I think ctrl and hover over shows up a mini dropdown where you can click on what you want to navigate to.
We need to be able to bring that up at code review and put it into the style guide.
You can also refactor it back.
maybe it's a weakness of an IDE?
I am 100% sure Idea have some solution too.
Navigate -> Implementations.
Works on interfaces/classes as well as methods.