[
  {
    "slug": "java-25-how-did-we-get-there",
    "title": "Java 25: How did we get there? (From OOP to DOP)",
    "description": "Discover the evolution of Java, from the object-oriented programming misunderstanding to the emergence of Data-Oriented Programming (DOP).",
    "date": "2026-05-17",
    "tags": [
      "java",
      "oop",
      "fp",
      "dop",
      "brian-goetz"
    ],
    "category": "Java",
    "content": "\nJava's OOP: A Historical Misunderstanding?\n\nWhen Alan Kay created Smalltalk in the 70s, his vision of \"message passing\" between autonomous objects departed from\nwhat the term \"Object-Oriented Programming\" would later come to mean. For him, the essence lay in \"message\npassing\" [1]: a decentralized system of autonomous cells communicating with each other, more inspired by biological\nsystems than a class hierarchy.\n\nJava, arriving in the 90s, took a more industrial direction: classes, encapsulation, mutable state, an explicit type\nhierarchy, and a portable platform. This choice was pragmatic and effective for the time, but it also created a lasting\nshift: we sometimes thought we were doing object-oriented programming in Alan Kay's sense, while we were mostly building\ncomplex object graphs where data and behavior were often too tightly linked to remain manageable.\n\nThis gradual success wasn't so much due to the \"purity\" of this model as to the technical promise of the JVM: \"Write\nOnce, Run Anywhere\". This success came with a certain architectural rigidity: the tendency to encapsulate\nall logic in classes, even for purely algorithmic treatments, even though static methods offered an alternative. While patterns like  or  have clearly\nproven their utility for structuring large systems, their systematic use sometimes led to \"over-engineering\" where\nbusiness logic disappears under layers of abstractions. We too often sought to solve state management difficulties\nthrough deep or fragile inheritance hierarchies, or through a multiplication of complex design patterns where a simple\nfunction would have sufficed.\n\n---\n\nThe Other Way: Lambda Calculus and Functional Programming (FP)\n\nIn parallel with the rise of imperative programming, another school of thought developed on much older mathematical\nfoundations: Alonzo Church's Lambda Calculus. Where OOP focuses on state and its mutation, functional programming (FP)\ntreats software as an evaluation of pure functions. Based on immutability and referential transparency, this approach\noffers appreciable reasoning comfort: a function called with the same arguments will always produce the same result,\nwithout unexpected side effects.\n\nThis philosophy radically transforms the development experience. It expresses itself well through the REPL (Read-Eval-Print Loop),\na powerful tool inherited from Lisp and Scheme traditions, later adopted by languages like Clojure and Python. Unlike OOP where testing a method often requires instantiating a\nclass and simulating a whole internal state (the \"tyranny of state\"), FP allows evaluating any fragment of logic\ninstantly. This fluidity is naturally found in testing: testing a pure function is simpler (input ->\noutput), reducing the need for complex mock frameworks and eliminating certain forms of boilerplate.\n\nYet, despite these advantages, FP was long perceived as difficult to generalize. Not because the model is impractical:\nmodern runtimes, persistent data structures, and optimizing compilers have reduced many of the costs. But computers\nstill largely follow the Von Neumann model, where mutating memory is a natural and very efficient operation. In practice,\npure functional programming therefore comes with trade-offs: additional allocation in some cases, recursion that must be\nhandled carefully, performance models that are less intuitive for many developers, and an entry barrier around abstract\nmathematical concepts. While languages like Erlang (used in telecommunications), Clojure, and Scala showed\nthe viability of FP, its mainstream adoption remains gradual, often through hybrid approaches rather than pure FP.\n\n---\n\nHybridization: The Best of Both Worlds\n\nFaced with the tension between dominant OOP and theoretical FP, a third way emerged: hybridization. Architects\nbegan to adopt the \"Functional Core, Imperative Shell\" pattern [3]: isolating business logic in a pure functional\ncore and managing interactions with the external world in a thin imperative layer. This approach found concrete validation\nwith React, showing that a \"flexible FP\" could simplify the construction of complex interfaces.\n\nUnder the impetus of Brian Goetz, Java began this pragmatic mutation. Goetz, while admiring Clojure's clarity [2],\nrefused to turn Java into a purely functional language. His approach was that of incremental evolution: identifying the\nmost useful functional patterns — those that actually reduce errors — and integrating them idiomatically. This\nfundamental work, begun with Lambdas in Java 8, laid the groundwork for a more radical transformation: the transition\nto data-oriented programming.\n\n---\n\nData-Oriented Programming (DOP): The New Face of Java\n\nThis is where Data-Oriented Programming (DOP) comes in [4], the practical fruition of this evolution. DOP prioritizes\nexplicit and immutable data structures, rather than hiding them in object hierarchies.\n\nJava 21 and following versions, up to Java 25 LTS, instantiate this vision through three pillars that stabilized\nprogressively:\n\n1.  Records: Finalized in Java 16, they are transparent, shallowly immutable data carriers that eliminate much of the\n    boilerplate found in traditional data classes.\n2.  Sealed Types: Finalized in Java 17, they allow modeling closed hierarchies, precisely defining the possible\n    alternatives for a piece of data.\n3.  Pattern Matching: Stabilized for  and record patterns in Java 21, then enriched in later versions, it\n    makes it possible to deconstruct and handle these structures precisely. Combined with Sealed Types, the compiler can\n    detect unhandled cases.\n\nIn concrete terms, modern Java DOP looks more like this than like a deep hierarchy of mutable classes:\n\n\n\nBy combining these elements, Java allows modeling application domains less as actor hierarchies, more as explicit\ndata structures. Rather than encapsulating data and behavior in the same class, we separate the structure of data\nfrom the logic that transforms it. This is a more direct approach and easier to reason about, combined with Java's type safety.\n\n---\n\nPerspectives: Java as a Mainstream Language Influencer\n\nBy becoming multi-paradigm, Java progressively redefines its positioning. Java, long criticized for\nits heaviness, now offers a balanced approach: the rigor of OOP for structuring systems, FP tools\nfor clarifying logic, and DOP for representing data explicitly. Java maintains its dominant role in enterprise\nsoftware while gaining recognition for these design choices. More than a quarter-century after its creation, Java continues to evolve,\nincorporating lessons from more recent paradigms while remaining pragmatic.\n\n---\n\nReferences\n\n[1] Alan Kay, on the term \"Object-Oriented\": \"I'm sorry that I long ago coined the term 'objects' for this topic\nbecause it gets many people to focus on the lesser idea. The big idea is 'messaging'.\" (Source)\n\n[2] Brian Goetz, interview with Nicolai Parlog (2021): \"Clojure is the most interesting language on the JVM\nbecause it is materially different from Java.\" (Video)\n\n[3] Gary Bernhardt, \"Boundaries\" (RubyConf 2012): Talk introducing the Functional Core, Imperative Shell\npattern. (Video)\n\n[4] Brian Goetz, \"Data-Oriented Programming in Java\" (InfoQ, 2022): Foundational article defining DOP principles\napplied to Java. (Article)\n",
    "readingTime": "6 min read"
  }
]