Do Primitives Need To Go?

John Hoestje Java, Opinion 3 Comments

I am currently working on an enterprise application using JSF as the view technology and JPA for the persistence layer. It may have been something in a backing bean or service method, but it struck me: Is there a good reason to use primitives anymore in enterprise applications?

When I started programming with Java around J2SE 1.2 (or was it J2SE 5.0, or maybe Java 2?), I only used primitives in my code. At that time, I thought it was nice I could rely on the default values instead of having to initialize something before I could use it. I didn’t know too much about wrappers at the time, and didn’t think I had a good reason to look into them any further.

I don’t have any clear memories of when I started to use the primitive wrappers, but I do remember when JDK 1.5 allowed for the automatic conversions between primitives and their respective primitive wrapper classes or commonly known as autoboxing/unboxing. This was nice as it didn’t matter if an int was passed to a method requiring an Integer or vice versa. It was syntactic sugar that seemingly made programming a little easier.

Frameworks

Even though Java enterprise frameworks such as JSF and JPA do a lot of work for you, it also exposes some of the weaknesses of relying on autoboxing and primitives.

These frameworks rely on the use of the primitive wrapper class instead of the respective primitive. If you use the JPA tool to create an entity based on an existing table, the tool will use the wrapper for the variable type. JSF inputs and converters operate on objects. With JPA, it has to. Remember the primitive defaults? A primitive’s default value can be confused with what is stored in the database.

If long was used as a simple entity id, the entity would have a default value of 0L. 0L is technically a value, so it makes it difficult to know whether the entity has been persisted or not. If id is of type Long, then it is easy to tell if the entity has been persisted since the id can now be null and therefore representing an un-persisted entity.

//id is long and entity is not persisted
long id = entity.getId();  //value of 0L
Long id = entity.getId();  //doesn’t help, still 0L

//id is Long and entity is not persisted
Long id = entity.getId();  //null
long id = entity.getId();  //what happens here?
//reference: https://keyholesoftware.com/2014/10/13/java-and-the-sweet-science/

Type Context

Primitive types do not adequately express the semantic context of the value. Let’s say we retrieve today’s temperature from a service and the returned value representing degrees is an int. Is it easy to determine what the context of int degrees is?

See Also:  Spring Boot & Apache Camel: Navigating the Data Processing Desert

First, is the value represented in celsius or fahrenheit? Second, what if the value is 0? You would have to assume the value was actually set and it isn’t the default value of 0. You may wonder why it feels like 70 degrees outside when the service says it’s 0.

Instead of using a primitive or even a primitive wrapper, the best solution may be to use a custom object that resolves to a proper value. For our value representing temperature, we could create a Fahrenheit object. This object would remove all doubt on the context of the value.

//
public class Fahrenheit {
…
	private Integer degrees;
…
}

Fahrenheit f = temperatureService.getDegrees(today);

//

A developer wouldn’t be able to accidently pass in a Celsius object when the method is expecting Fahrenheit.

//
…
public Celsius convertDegrees(Fahrenheit f) {
…
}

//only this would work
Celsius c = temperatureService.convertDegrees(f);

//

Using a primitive as a return value can also be ambiguous. Returning a boolean to represent if something worked or not doesn’t necessarily represent what did happen. Using a Result object is more descriptive by containing more information.

See Also:  React vs. Angular: A Comparison Between Two Great Options

Mocking

While mocking frameworks can handle primitives, mocking frameworks like to work with objects. The code snippet below is the the Expectations class in JMock. As the code snippet shows below, some autoboxing magic is allowing the framework to set the expectation that a int value of 6 will be returned.

Expectations.returnValue(Object result):        

  context.checking(new Expectations() {
 	{
   	atLeast(1).of(serviceMock).getPrimitive(5);
            	will(returnValue(6));
….
}

JMock seems to handle primitive arrays properly since the object for arrays is dynamically created.

Expectations.returnValue(Object result):        

  context.checking(new Expectations() {
 	{
           	int[] ints = { 2, 3 };
            	atLeast(1).of(serviceMock).getPrimitives(5);
            	will(returnValue(ints));
….
}

Even though primitives and primitive arrays worked with mocking frameworks, it feels like you are not working with the real values. Your parameter is an int, but the mocking framework would like you to use Integer.

Thoughts

Looking back at the history of the primitive wrappers, the increased functionality of the wrappers over time, autoboxing/unboxing, and reading articles from developers from other languages, it feels like primitives could have been left out of the initial implementation of Java. Java is an object-oriented language, but contains non-object primitives. The autoboxing feature feels more like a band-aid than a real solution.

I am making this specific to enterprise applications due to the fact that enterprise applications have access to generous amounts of resources. The environment makes it easier to create descriptive code that use of objects like Integer and Fahrenheit. In constrained devices where garbage collection costs and heap size matter, primitives make sense and hold an advantage over object counterparts.

— John Hoestje, [email protected]
[thumbs-rating-buttons]

0 0 vote
Article Rating
Subscribe
Notify of
guest
3 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments