Over a million developers have joined DZone.

How to Manage Dynamism in Java

DZone's Guide to

How to Manage Dynamism in Java

Are you having trouble with dynamism in Java? Check out this post on dynamism and static typing in Java applications.

· Java Zone ·
Free Resource

How do you break a Monolith into Microservices at Scale? This ebook shows strategies and techniques for building scalable and resilient microservices.

Dynamism can creep into Java code bases for various reasons. It can get in the way of reaping the many promised benefits of a statically-typed language like Java. Especially with legacy code bases, the practice can be extensive and difficult to come out of.

Java is a statically-typed language. It forces all data to be stored in somewhat structured objects, and all methods to be attached to one object or the other.

But, it doesn't mean that the language can force good class design and ensure that the right type of object is used for each task. In fact, it often doesn't even make it too easy to do the right thing.

A DSL (domain-specific-language) or a visual rule builder could probably do a better job in both the above aspects, because they are purpose-built. But, they are likely to be limited in scope. Especially with rule engines, many of them provide escape hatches, such as scripting hooks. Unless the tool is well-designed, the script snippets can eventually grow into complicated, unmaintainable code bases, which can lead to costlier mistakes.

How Dynamism Creeps Into an Application

The amount of static typing help that the language can provide depends on how strictly types are declared. For example, the language cannot help much if type information is diluted:

public class CustomerDetails{
    private Object id;//Long or Integer or String?
    private Object dateOfBirth; //Long millis or Date?

It gets even more diluted with the misuse of collection objects.

Map<String, Object> custDetails = new HashMap<>();

...Later in code...
custDetails1.get("id"); //Long or Integer or String?
custDetails1.get("dateOfBirth"); //Long millis or Date?
custDetails1.get("DateOfBirth"); //Spelling error

And, some types, which are layer-specific, creep into the application logic.

JSONObject custDetails = new JSONObject();
custDetails.put("id", ...);

...Later in code...
custDetails1.optLong("id"); //Am I sure its Long?

And, less commonly, there is an incorrect use of inheritance for convenience.

public class CustomerDetailsBean extends JSONObject {
  public String getId(){
    return optLong("id");

...Later in code...
custDetailsBean.put("id","..."); //Base class method still available 

The application is statically typed only if the domain objects are well-defined, w.r.t. type information.

Consistent under-typing across the code base, especially for domain objects, can pretty much make it a dynamic application in Java. This is the primary entry vector for the problem of too much dyamism.

Of course, even with a fairly dynamic code base, the project can produce benefits from the static typing practice in the Java ecosystem; for example, the core of the language is statically typed, and so is the Java collections framework, as well as the various open-source libraries we use — not to mention the most important parts, including the Web server, drivers, and connection pools. But, the benefit does not cover the application's business logic.

Is Static Typing Worth the Effort?

Static typing brings a few benefits as well as some annoyances. It really depends on the scope and complexity of the project and whether static typing is worth the effort. Here are some of the important benefits:

  1. Compile-time error checking
  2. Tooling features like code-completion and refactoring
  3. Functional traceability

But, it comes with its share of disadvantages:

  1. Verbosity
  2. As the design evolves, it regularly requires changes in class design and method signatures.
  3. It needs some bits of transformations and copying at layer boundaries (eg. user interaction, database).
  4. The safety features are not fool-proof. We have to know the limitations.

If the application is relatively simple or a quick automation script, you can dial down the static typing and use collection objects, or even pick a dynamic language like Python.

But, if the application is fairly complex and has a solid feature roadmap, static typing can save a lot of trouble later at the cost of some upfront effort.

There Is a Place for Dynamism

Obviously, there is a place for dynamism. The real world does not care much about an application's domain model, and most external interfaces are going to involve some amount of dynamism.

Even within the domain model, there are cross-cutting concerns, such as logging, authentication, authorization, transactions, query views, filter conditions, etc., which form the infrastructure code. These again require some amount of dynamism.

The challenge is to, for each aspect, identify the right design patterns and libraries to keep dynamism within a limited scope. For example, the challenge is to get from the dynamic object to the static one as early as possible:

 request.getParameter("abc") to myFormBean.setAbc(abc) 
 resultSet.getString(NAME) to myDto.setName(name) 
 jsonObject.optString(NAME) to myBean.setName(name) 

Much of this work can even be automated using appropriate libraries or small custom utilities targeting each aspect of the transformation individually. The solution can be as simple as Bean adapters, which allow you to access a bean's property by name or custom annotations or picking the right library.

The Performance Cost of Static Typing

The performance costs of static typing are often misunderstood or overestimated. The best thing to do is implement the solution within a limited scope and run benchmarks (for example, implement for a particular module or task and run benchmarks).

Static typing often results in:

  1. A large number of small classes, eg. form beans, API beans, DTOs, adapters, etc.
  2. Copying/transformations between these beans across the request lifecycle
  3. Some use of Java reflection API (example Jersey, GSON, JPA)
  4. Build a step like code generation/byte-code enhancement
  5. Lots of "new" objects instead of passing around the same mutable object

However, static typing can be several times faster, because it:

  1. Avoids comparatively costlier collection objects, like HashMaps for passing around data.
  2. Avoids comparatively costlier operations, like string parsing, joining, regex testing.
  3. Avoids large business classes that are loaded early.

As always, the biggest problem with performance tuning is a large number of performance myths and outdated performance advice. A more reliable method would be a limited implementation or quick prototyping followed by measurement.

Developer Effort Involved in Stricter Static Typing

Implementation of strict static typing can be a tedious task, as there can be a bit of boilerplate code involved. But, by using the right frameworks and libraries, it can take away much of the effort. Even in-house frameworks can be built to address individual aspects of work to make the development process easier.

However, static typing can also increase developer productivity several fold, because there is:

  1. No unnecessary type casting
  2. Easy code introspection and traceability (find references)
  3. Syntax-highlighting and code-completion
  4. Cleaner code base and a correct place to do each operation (ideally)
  5. Plays well with other coding best practices, like a layered approach and separation of concerns

Final Thoughts

All of us face the fear of introducing a new feature and not understanding the full breadth of the impact. Every corner case can fail because of a new feature. This is especially true of legacy code bases. If it happens pretty often, you should probably think of (micro) refactoring stricter static typing into the application's domain objects.

Getting to Implementation

This post just scratches the surface. There are more aspects to study and understand while getting the hands dirty and implementing the changes, including:

    1. Places where static typing drops the ball and how to track them;
    2. Design patterns for supporting dynamism cleanly;
    3. Layer-specific libraries, which help with bridging the static: dynamic gap;
    4. Java performance hacks and myths;
    5. Micro-refactoring to a cleaner domain model.

How do you break a Monolith into Microservices at Scale? This ebook shows strategies and techniques for building scalable and resilient microservices.

java ,dsl ,dynamic typing ,static typing ,legacy code ,tutorial

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}