Featured image for “Programming Language Assimilation: Be the Borg”

Programming Language Assimilation: Be the Borg

October 31, 2023


One of the great benefits of learning multiple programming languages is the ability to learn how languages are different from one another and what the best pieces of each language are. I’m not deep into Star Trek, but the Borg Collective fascinated me. Before assimilating a new species, the Borg assessed if the new species was worthy of assimilation. To be worthy, the species’ assimilation would need to get the Borg closer to perfection.

We can think about new programming languages like the Borg treats new species. When we learn a new language, we should measure success based on how it hones our programming tool kit and how its concepts grow our overall programming knowledge and understanding.

When learning a new language, the desired end result is not to continue using the new language indefinitely but to assimilate the best pieces of the new language into your programming mind’s hive. This idea of assimilation also applies to architectural designs. We can assimilate microservices and apply some of the principles to a monolithic application.

In this blog, I’ll give you an example of this idea. In a recent assimilation, I was able to spend a few months learning and working with Dart in a Flutter application. Even though I no longer use Dart itself on a daily basis, the concepts I learned and assimilated strengthened how I handle nulls in Java.

Null: Assimilating Dart to Java

Earlier this year, I had the opportunity to work with Flutter and the Dart language. Through his experience, I became much more aware of how I work with nulls and was able to assimilate concepts I picked up in Dart to apply them in Java (my programming language of choice).

Dart 3 provides null safety, which prevents errors resulting from unintentional access of variables set to null. It’s awesome. The developer has to intentionally add ? to indicate a variable is nullable. The developer also has to intentionally add ! to mark a nullable variable as nonnull in a statement.

Without a safety feature like Dart’s, I commonly find it boils down to one statement that determines whether a null is introduced or not. An easy example would be returning an entity by the data source. Do you return a null for any value not found by the data source, or do you do something else?

Returning a null puts the responsibility of null handling somewhere else… usually somewhere not as adept at handling a null. This, in turn, makes it easy to introduce the dreaded NullPointerException. While returning a null is easy, it may not be a good solution.

Returning to the language that I have used the most in my programming career, let’s see how we can assimilate the usage of null in Java. As with most things programming-related, there isn’t an easy, catch-all solution. However, the following examples are solutions to fit some specific scenarios.

Null Replacement: Optionals

Java introduced Optional waaaay back in Java8. Optional is a container object that may contain a null. Optional allows the logic to ask questions about a real object before operating on something that may not exist.

Optional makes the code less brittle, so it holds up when something isn’t found or when calling a method returns a null. It makes the code more succinct and readable when making a decision based on the presence of a value.

Let’s look at an example. This code…

public Foo findFooByDate(Date) {
….
if(rs.size() == 0) {
	return null;
}
….
return foundFoo;
}
…
Foo foo = findFooByDate(date);
if( foo !=  null) {
	//.. do something with foo
}

With Optional becomes…

public Optional<Foo> findFooByDate(DateTime dateTime) {
….
if(rs.size() == 0) {
	return Optional.emtpy();
}
….
return Optional.of(foundFoo);
}
…
Optional<Foo> optionalFoo = findFooByDate(date);
if (optionalFoo.isPresent()) {
	Foo foo = optionalFoo.get();
	//.. do something with foo
}

We can also program if you want a default behavior if the returned value is null…

	Foo optionalFoo = findFooByDate(date).orElseGet(createFoo());

Or if you want to hide null from someone else’s method…

	Optional<Foo> optionalFoo = Optional.ofNullable(notMyGetFooMethod(date));

While the Optional handling logic might look like handling a null, the benefit of returning an Optional is making the calling code explicitly think about the existence of a value. Returning a null allows the possibility of the calling code to be lazy and not handle a missing value scenario. With Optional, you are telling the developer that this call may not return a value, so please handle it accordingly.

Null Replacement: Exceptions

Another option is to replace null with an exception. There is an expense associated with creating an exception with a stack trace, so make an informed decision on when to use exceptions.

Here’s our example…

public Foo getFoo(id) {
….
if(rs.size() == 0) {
	return null;
}
….
return foundFoo;
}

Becomes…

public Foo getFoo(id) {
….
if(rs.size() == 0) {
throws new FooNotFoundException(id);
}
….
return foundFoo;
}

Null Replacement: Null Object Pattern

Recently, when working with Spring Security and looking at request caching options, I came across an implementation of ServerRequestCache called NoOpServerRequestCache.

Instead of returning a null ServerRequestCache to handle situations where caching isn’t wanted, a Null Object is returned instead that implements default behaviors. In situations where the code needs to know if a real or null object exists, a null check method is used.

	class RealBar implements Bar {
		…..
		@Override
		public Boolean isNull() {
			Return Boolean.FALSE;
		}
	}

	class NoBar implements Bar {
		…..
		@Override
		public Boolean isNull() {
			Return Boolean.TRUE;
		}
	}


Null Replacement: Default Value

Similar to Martin Fowler’s refactor suggestion, you could use a precheck and return a default value instead of relying on exceptions for logic flow.

To use Apache StringUtils.indexOf() as an example:

StringUtils.indexOf(null, *) = -1

The logic is something like:

	public static int indexOf(CharSequence seq,
                          CharSequence searchSeq) {
		if(seq == null || searchSeq == null) {
			return -1;
		}
		….
	}

Conclusion

Learning new languages or design patterns isn’t the easiest thing to do. It takes time and effort to get deep enough into learning new technology to assess what to assimilate into daily programming.

When I was learning Dart, the handling of null values wasn’t one of the things I was expecting to assimilate! However, it’s a concept that I quickly realized will help me write better Java code.

Be patient with yourself, and remember to always be looking for practical implications. The Borg didn’t get that fascinating by assimilating only a few species and they didn’t do it in a year either.

Have some programming language assimilation examples from your own work? I want to hear! Share in the comments below, and follow the Keyhole Dev Blog for more.

About The Author

More From John Hoestje

About Keyhole Software

Expert team of software developer consultants solving complex software challenges for U.S. clients.

Share This Post

Related Posts


Discuss This Article

Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments