DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
  1. DZone
  2. Coding
  3. Languages
  4. Creating Immutable Objects Run-Time

Creating Immutable Objects Run-Time

Peter Verhas user avatar by
Peter Verhas
CORE ·
Nov. 16, 13 · Interview
Like (0)
Save
Tweet
Share
12.69K Views

Join the DZone community and get the full member experience.

Join For Free

Java supports immutable variables, in the form of final modifier for fields and local variables, but it does not support the immutability of objects on the language level. There are design patterns that aim to distinguish mutator and query methods in objects, but the standard library and libraries from different sources may not support the feature.

Using immutable objects makes the code safer because they reveal programming mistakes manifesting run time sooner. This is the so called “fail-fast” principle that you can certainly understand and appreciate if you came from the C or C++ programming field to Java. If you can have an immutable version of an object and you pass it on to a library (be it external or your own) an exception occurs as soon as the code tries to call any method that is a mutator. Having no immutable version the error such a call causes manifests much later when the program fails with the modified and thus presumably inconsistent-state object.

Because of these advantages of immutable objects there are libraries that deliver immutability for some special cases. The most known and most widely used example is the Guava immutable collection library from Google. This creates immutable versions for collections. However collections are not the total world of Java classes.

When you have the code under your own control you can split your interfaces to a query and a mutator part, the mutator eventually extending the query interface. The implementation can also be done in two classes: a query class implementing the query interface, and a mutator class extending the query class implementing the mutator interface (that also includes the query interface functions). When you want an immutable version of an object you cast it and pass on using the query interface. This is, however not 100% security. The library can, by sheer ignorance of the code or by mistake, cast the object back and mutate the object state. The fool proof solution is to implement the query interface in a class that is set up with a reference to mutable object and implementing delegation to all methods defined in the query interface. Though this is cumbersome to maintain such code in Java in case of numerous and huge classes the solution is generally simple and straightforward. You can even generate the delegating query implementation (extending the mutable class) when the query/mutator interfaces, and class implementations are not separated.

The project Immutator delivers this functionality during run-time. Using the library you can create a delegating proxy class during run-time that will extend the mutator class and will pass the method calls to the original object when the method is considered query but throw a runtime exception when the method is considered to be a mutator. The use of the class is very simple, all you have to do is to call a static method of the Immutable class:

MyMutatorClass proxy = Immutable.of(mutableObject);

The generated proxy will belong to a class that extends the original class mutableObject belongs to, therefore you can pass along proxy to any code where you would pass the mutableObject but you do not want the code to alter the state of the object.

How does the library know which methods are query and which methods are mutators? The library immutator in this simple case (there are more complex calls if the simple case is not sufficient) assumes that any method that is voidis also a mutator, and any method that returns some value is a query method.

To support the ever increasing popularity of fluent api the call can be written in the form:

MyMutatorClass proxy = Immutable.of.fluent(mutableObject);

in which case any method that returns a value compatible with the class of the argument is also considered to be a mutator method.

If even this functionality does not describe the behavior of the class to proxy then the general form of the call is:

MyMutatorClass proxy = Immutable.of.using(Query.class).of(mutableObject);

which believes that any method defined in the interface Query is a query and the methods that do not present in the interface Query are mutators. Using this form an query proxy can be created for any objects.

This is nice and interesting. Having said all that there are some limitations in the implementation of the library that partially come from the Java language and from the available JDK.

You can not declare any final method as mutator method. The reason for it is that the generated proxy class has to extend the original class so that the proxy object can be used at the place of the original object. It can not, however override the final methods. Final methods are actually not proxied, but execution is passed directly to the original method. This is how Java works.

The proxy object is created in Java source and compiled during run time. This may be slower than, for example using cglib that uses the asm package and generates byte-code directly. On the other hand the library may be more resilient to Java version changes and it is easier to have look at the internal working of the library and the proxy.

Last, but not least the library uses some unsafe package calls (google that if you need), that may not work on all platforms. This is needed to create the instance of the proxy object. Since the proxy class is the extension of the original class creating a proxy object the “normal way” would implicitly invoke the constructor of the extended class. This may not be a problem, but in some cases, when the constructor does some heavy duty work, this may be.

Knowing all those incorporating the library into your application is very simple. Since the com.javax0 libraries are stored in Sonatype repository all you have to do is inserts the library as a dependency into your pom.xml file as

                         <dependency>
                             <groupId>com.javax0</groupId>
                             <artifactId>immutator</artifactId>
                             <version>1.0.0</version>
                         </dependency>

and stay tuned for upcoming releases.

Object (computer science) Database Library Interface (computing) Java (programming language)

Published at DZone with permission of Peter Verhas, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • The Future of Cloud Engineering Evolves
  • Better Performance and Security by Monitoring Logs, Metrics, and More
  • Distributed SQL: An Alternative to Database Sharding
  • How To Use Terraform to Provision an AWS EC2 Instance

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: