E.g. Effective Java is a requirement inside Google, so even public GDrive APIs have final classes. External APIs is exactly the thing you'd want to mock.
E.g. Effective Java is a requirement inside Google, so even public GDrive APIs have final classes. External APIs is exactly the thing you'd want to mock.
Of course you get code bloat defining interfaces for everything you intend to implement once, and you have to enforce these rules, but this is something that could be made easier. Not in Java, but imagine a language where:
- Concrete classes can only be used in new, or in some platform provided DI container.
- Methods can only accept interface types and return interface types.
- Fields are private only, all public/protected is via properties (or getters/setters, it just has to be declarable in an interface)
- You have a ".interface" syntax (akin to ".class" but for types) that refers to the public members of a class without tying you to the concrete class itself. You can use this as a shorthand instead of declaring separate interfaces for everything.
Eg.
```
final class GDrive { ... }
public Download file(GDrive.interface drive) { ... }
class MockDrive implements GDrive.interface { ... }
```
The closest I can think of is a hypothetical typed variant of NewSpeak, but maybe something like this exists already?
https://github.com/google/auto/blob/main/value/userguide/ind...
Interfaces with one implementation are terrible. They just clutter everything and make the code navigation a pain, so it's good that people are avoiding them.
Perhaps a special "test-only" mode that allows to patch finals is a better idea.
I mean, using this logic, every single function can be hidden behind an interface. Even the sole implementation of the interface can be hidden behind a yet another interface.
If there's just one implementation, then the interface is not necessary!
- Very broad, unspecific contract that may even obscure the methods purpose
- You cannot modify the contract without modifying the class AND vice versa
- Shrinking a contract (taking away elements) is far harder and more likely to cause breakages in other code than growing a contract
- Mocks become more cumbersome because the contract is so broad
- Changes to the concrete class cause ripple effects in code that doesn't care about the change
If you 100% sure the one implementation will never change, then I'd say you're right. But it requires future-telling.
And this basically never happens, but you still have to carry that extra overhead of interfaces.
Java also supports private/public method visibility, and this can be used to clearly show the contract. No need for interfaces.
They intend to write a test double which implements the interface. But then they don't get around to writing tests?
I can no longer recall exactly what Bloch said, I may have to search through some of by old writing to find it, but at one point he admitted he didn’t really understand type theory when he designed the collections API. And while I appreciate the honesty, and the reason (he was trying to illustrate that this stuff is still too hard if “even he” didn’t get it), I think it paints him rather worse.
But I already knew that about him from working with that code for years and understanding LSP, which he clearly did not.
I don’t know why they thought he should be the one writing about how to use Java effectively when he was materially responsible for it being harder to use, but I’m not going to give him any money to reward him. And there are other places to get the same education. “Refactoring” should be a cornerstone of every education, for much the same reason learning to fall without hurting yourself is the first thing some martial arts teach you. Learn to clean up before you learn to make messes.
He said at one point that he had thought of a different way to decompose the interfaces for collections that had less need for variance, with read and write separated, but he thought there were too many interfaces and they would confuse people. But when I tried the same experiment (I spent years thinking about writing my own language)… the thing is when you’re only consuming a collection, a lot of the types have the same semantics, so they don’t need separate read interfaces, and the variance declarations are much simpler. It’s only when you manipulate them that you run into trouble with Liskov, and things that are structurally similar have different contracts. The difference in type count to achieve parity with Collections was maybe 20% more, not double. So to this day I don’t know what he’s talking about.
Most APIs should only consume collections from callers, so friction against mutation in your interface is actually a good thing.
> the thing is when you’re only consuming a collection,
> a lot of the types have the same semantics, so they
> don’t need separate read interfaces
> Most APIs should only consume collections from callers
I'm having trouble understanding what you mean by "consuming a collection." Can you expand?So Josh Bloch opted against separate read/write interfaces.
> the thing is when you’re only consuming a collection, a lot of the types have the same semantics, so they don’t need separate read interfaces, and the variance declarations are much simpler.
And you opted against separate read/write interfaces.
Normally a JDK 21 would let you get private/final reflective access to your own “module” but not to the stdlib modules but so many libraries want private access to stdlib objects such as all the various date and time objects.
What ones have you ran into? Jackson doesn’t even require you to —add-opens on anything.
> And mocking tools have to resort to bytecode manipulation to mock the final classes
Well which is it? Presumably you use said mocking tool anyway, so it's not your effort that's being expended.
"Final all the things" really doesn't go far enough. There is little point substituting a mutable hashmap for a "final" mutable hashmap, when the actual solution is for the standard library to ship proper immutable collection classes.
In any case, I prefer to avoid mockito anyway, so it's a non-issue for me. Just do plain ol' dependency injection by passing in dependencies into constructors.
And I’ve never forgiven him for it
Google uses mocks and fakes implementations of interfaces, and provides dependency injection frameworks for managing these (Guice and Dagger).
It was garbage.
I don’t recall doing it for domain objects though.
Mocking final classes is a blunder on its own. Most classes should be package private not public, so being final would have close to zero relevance. Personally I do not use mocking tools/frameworks at all.
OTOH, there is very little benefit of having final classes performance wise. Java performs CHA (class hierarchy analysis), anyways.