2

What I learned about Java from Clojure

 1 year ago
source link: https://devm.io/java/java-clojure-lessons
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Insights from the other side

What I Learned About Java From Clojure

21. Sep 2022


It's 2012: Curiosity lands on Mars, Windows 8 is released, the first part of "The Hobbit" hits theaters, the Beastie Boys break up, and Germany once again fails to win the European Championship. Excluding the Mars landing, this is (subjectively) a year full of disappointments. Apparently…

That same year, I learned a lot about software development with the not-so-old Java 7 and Java EE 6 at my job. So, while I was busy with application servers, EJB, JPA, and JSF, and adding the "Gang of Four" patterns to my bag of tricks, I happened to stumble across Clojure on a forum - a JVM-compatible language that seemed to think very little of my painstakingly learned OOP and Java skills: no static typing, no classes, immutable data structures, or functions at the heart of the language. After digesting the initial culture shock, I became increasingly involved with the language concepts in my private work. I noticed that some problems, but not all, could be solved better in Clojure. This was not always due to just the language itself, but also its idiomatic approach. Over the last few years, some of these paradigms seeped into my Java code and greatly shaped my image of what is good, maintainable code. In this article, I would like to highlight some of these concepts. Of course, the goal isn’t writing Java code that only Clojure developers can understand, or deviating completely from Java's paradigms. No Clojure knowledge is needed to follow this article.

Avoiding mutable state

When getting started with Clojure, you will quickly notice that state is handled much differently in the application than in Java. This is caused by Clojure's immutable data types. If a function is applied to them, it returns a new data structure and leaves the originally passed one unchanged. The example in Listing 1 shows that the data structure behind the symbol _my-vector _cannot be manipulated afterward. The increment is applied to each element of the vector in the second statement, resulting in a new data structure. Therefore, developers can pass a data structure to further functions at any time, without hesitation. You can be sure that no operation will manipulate it. Changing this passed data has several disadvantages: Parallelized execution of code is more difficult as the risk of Race conditions cannot be ruled out. After all, no one can say which called code modifies the object and when. A customized code point could suddenly cause errors in a completely different part of the code without developers expecting it. Rich Hickey, the inventor of Clojure, addressed this problem in detail at Java One in his talk "Clojure Made Simple" [1]. Your first step on the way to a more robust data model is abandoning an anemic data model like Java Beans, where each field has getters and setters. The _Square _class in Listing 2 is an example of an immutable data structure in Java. Leaving Reflection aside, once an object of this class is created, it cannot be changed.

Listing 1

; Assign a vector to the symbol my-vector
(def my-vector [1 2 3 4 5])


; Apply the inc function to each element
(map inc my-vector)
-> [2 3 4 5 6]


; Output of the original vectors
my-vector
-> [1 2 3 4 5]

Listing 2

class Square {


  private final int x;
  private final int y;


  Square(int x, int y) {
    this.x = x;
    this.y = y;
  }


  public int getX() {
    return x;
  }


  public int getY() {
    return y;
  }


}

In Java, code can be written defensively, but it’s not always possible to prevent an object from being manipulated later in your work. Nested object structures and collections offer a lot of room for (unintentional) state manipulation of an object. Ultimately, it only helps determine the team’s discipline and the use of libraries and frameworks that do not manipulate transferred objects. There are still libraries and frameworks that expect the Java Bean structure, in terms of mapping, persistence, or data serialization.

Concatenate calls, don’t nest them

In enterprise Java projects, the concept of layer architecture is widespread. The idea behind this is to structure classes in the project according to their technical affiliation. Classes only get access to the next layer to be used in order to keep dependencies at a minimum. Usually, the reference to the responsible object is passed to the next layer with the dependency injection, and is then used in the program’s code. A simple example of this procedure can be found in Listing 3. There is no comparable concept in Clojure. But in my early days, I tried to reproduce this layered model first (Listing 4) and...


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK