I’ve been lucky enough to spend the past year working professionally with Clojure, and it’s been an absolute joy. I’ve recently moved teams, and I’ll be working with Java for the foreseeable future. Any tips or resources for a Clojure-minded developer looking to quickly ramp up on Java? And, following that, any tips for approaching Java in a Clojure-y way? What should I seek? What should I avoid?
Make new luck sir!
I’m too new to Clojure to make any ‘Clojure-y’ tips, but for Java itself, Joshua Bloch’s Effective Java is a must read (and an enjoyable one).
I never worked professionally with Java and became curious to learn more about the language this year. And my method is rather the same for any programming language/tool that I want to learn: i) read books and ii) implement a project (usually one I already have). The list of books read:
The first book I read was the Effective Java but it was not at all useful to me as first contact with the language, but I would definitely came back to it if I got myself working in a professional environment.
A have a CLI app that I made to manage my own books written in many languages multiple times (the only public version is this one) and now Java. I also created a repository where I re-wrote every example from HFDP in Clojure here because I found easier to reason about them while fiddling with Clojure and was interesting being free to experiment with the language to do tasks that I would normally not approach in the way implemented in this repo.
I too have had to move from (recently) writing mostly Clojure (back) to mostly Java, and though it’s painful in many ways it’s given me a better appreciation of both languages I think - particularly the hidden polymorphisms that Clojure hands you for free that you only really value when they’re gone. Generally speaking Java makes me think far harder to do anything as there seems to be so many bear trap choices available, which need much more unwinding when they go wrong.
I have been using/exploring the following, with varying levels of success getting adoption from colleagues:
Clean Architecture - I think Clojure apps would benefit from this approach too to separate inner domain logic from interfaces and adapters. For Clojure it’s made me consider using interfaces / multimethods more in future. Tends to (arguably) encourage separation of DTOs from Logic classes - especially when crossing layer boundaries - so not very OO
Lombok - @Builder allows better “growth” because it avoids positional arguments in constructors that break as soon as you add anything to e.g. a DTO. Also @Getter @ToString et al just saves a lot of code.
I have actually tried to be spec-like and encouraged DTOs to have reusable getters by extracting them each out to individual interfaces, and putting javax validation annotations on the interface themselves. This encourages making the attribute and not the entity the first class citizen, and ties validation to the attribute. (No generators though!) It seems nice as many getters are reused again and again. It does mean avoiding making these types Optional, (cf “Maybe Not”) because the getter may be Optional in some DTOs and not in others, but @NotNull (or a variant of such) can be added to concrete classes implementing the getter interfaces. It doesn’t stop having to bake in concrete types everywhere unfortunately so it’s a long way from the reusability of keywords and spec but it’s something I’m playing with. Any thoughts on this are very welcome
ECS (Entity-Component-Systems) - The ultimate Clojure-y way to doing DTOs in Java? Avoid them and use use instances of an Entity class that has 2 properties: Uuid id and List<? extends Component> components. Arose out of Gaming (C++) where “GameObjects” are notoriously polymorphic. “This mushroom is also a weapon, a potion and warrior when wet.” Again strictly separates data (Entities with components) from logic (Systems). But to me Entities with components are close to the (Datomic-inspired) entity-attribute-value model and (just maybe…) does away with the horrific “Death by specificity” of baked in concrete types that Java apps typically suffer from. Very non-idiomatic though - I can’t see myself convincing anyone of this easily but would love to try it! Not great for highly structured/nested data however. This is a nice talk on the subject https://youtu.be/SFKR5rZBu-8
What a wonderful response, thank you. These are exactly the kinds of suggestions I was hoping for.
My tip for that would be to not do it. Just like Clojure is not OOP and shouldn’t be treated as such, Java is not FP and should not be treated as such. It’s added FP-ish stuff, like lambdas and streams, and they’re notoriously slow. Granted, people use Python so speed isn’t always important, but it’s something to keep in mind. I did problems on HackerRank a while back, which requires your solution to run under a certain amount of time, and streams couldn’t be used because they’d slow your code down too much.
Whatever code you need to write, instead of trying to write it in Clojure and port that to Java, think about the algorithm itself and look up how that algorithm would be done in Java. Google and Stack Overflow are you best friends there. Idiomatic Java isn’t even remotely similar to idiomatic Clojure, so just be open to doing things in a different way.