Kawaii is a small library aimed at exploring microtyping in Java - below I set out what microtyping is, the problems with using it in Java, approaches for minimising these problems, and possible ways forward.
What is microtyping?
"Microtyping", also known as "tiny typing", refers to a pattern in which code avoids using primitive classes such as String or Integer and instead uses "microtypes" which wrap a primitive value to confer a specific type identity to it, and is seen as the cure to primitive obsession. For example, instead of:
by using microtyping we can write:
Microtyping allows us to extend the type system to confer the advantages of typing on primitive values:
Microtyping allows us to extend the type system to confer the advantages of typing on primitive values:
- Making the code self-documenting.
- Allowing the compiler to offer better compile time security and other tools to offer better inspection/refactoring support.
- Allowing specific microtypes to define validity rules - eg an Integer microtype that allows only positive values.
Why is this a problem in Java?
I have seen microtyping used from time to time, and it's been discussed before, but my experience microtyping is not common in Java. This is because, unlike some other languages (Ada, D, Go etc), microtyping in Java is unwieldy:
- Primitive types are final (for security amongst other reasons) and cannot be subtyped.
- But type system offers no alternative to subtyping, ie no alias or typedef functionality.
And as far as I can see, neither new language functionality in Java 8 nor proposals for Java 9 improve on this, although I'd love to be proved wrong.
The only option this leaves is wrapping the primitive inside another class - but this can potentially involve a lot of boilerplate code when creating, and clumsy integration when using.
The only option this leaves is wrapping the primitive inside another class - but this can potentially involve a lot of boilerplate code when creating, and clumsy integration when using.
Boilerplate
Although Project Lombok reduces the boilerplate when creating value classes, and Java 8 add some interesting possibilities for value object creation, values objects are not microtypes:
- A value object will typically wrap one or more primitive values or other value objects - but it won't act as it if it is a single primitive value.
- A microtype will always wrap a single primitive value, and to some extent will be interoperable with it.
In other words, a microtype can be seen as special case of a value object, but with additional library integration.
Given this, Kawaii uses an abstract base wrapper class (MicroType.java) which is extended to create a hierarchy of microtypes - for example JSONString.java which extends MicroType<String> (JSONString is abstract, since we would expect other specific concrete JSON classes to extend this, for example PersonJSONString.java).
The major boilerplate issue here is that due to Java inheritance rules, each subtype needs a constructor. Lombok could be used, but given that most IDE's will auto-generate the class and constructor, and once created it's not going to need to be looked at again, I don't feel this is actually a practical problem.
Integration
Since Kawaii wraps rather than extends primitives, it's not possible to use the microtypes as direct replacements for primitives when calling Java or 3rd party libraries. For example, given a microtype "Name" extending MicroType<String>:
- Can't call "doSomething(String)" directly - have to unbox by calling "name.value()"
- Can't infer method returns of String to Name - have to box by calling "new Name(value)"
This has the potential to pollute code, but by pushing the boxing/unboxing down as far as possible, and using adapters for external libraries, the issue can largely be avoided.
For instance, JacksonJSONTest.java demonstrates how:
- ClassPathResources.java can be used to allow reading a file from the classpath into a MicroType<String>.
- KawaiiObjectMapper.java can be used to read and write from a Java POJO that contains microtypes to a JSONString microtype, using the Jackson JSON processor.
- A custom Jackson serializer (MicroTypeSerializer.java) ensures that JSON produced when Jackson serialises a POJO containing microtypes is the same as if it had contained primitives.
resulting in code which does not use the String primitive directly at all:
Note that in cases where a method returns a new microtype instance, it is necessary to pass in the class, so for instance instead of "writeValueAsString(Object)" we have "writeValueAs(Class<? extends JSONString>, Object)". New microtype instances are reflectively created in these cases using the static helper MicroTypes.java.
What next?
Firstly, some questions I do not intend to answer in this post:
In my view, the major hurdle required to move forward is adopting a standard approach. Short of adding language-level support in Java, the next best would be to have a MicroType class in the JDK libraries, in the same way Optional was added in Java 8. This would allow 3rd-party libraries to add support, as is already happening for Optional. I could of course continue to expand Kawaii by adding adapters for other libraries (JAX-RS, JDBC, JAXB etc), but this will always be inferior to the libraries directly supporting a standardised MicroType.
Comments? Is microtyping an idea whose time has come in Java? Or it it's practical utility to small for it to ever catch on?
- Is all this extra effort worth it? This is a subjective question and depends for instance on the size of the team/codebase, complexity, style etc - but for the purposes of this discussion I will assume there are at least some circumstances where it is.
- Why not just use language X which already supports microtypes? For some projects this may be an option, but choice of languages depends on many factors. I will assume there are at least some existing Java projects which will benefit, or new projects which will use Java for other reasons.
In my view, the major hurdle required to move forward is adopting a standard approach. Short of adding language-level support in Java, the next best would be to have a MicroType class in the JDK libraries, in the same way Optional was added in Java 8. This would allow 3rd-party libraries to add support, as is already happening for Optional. I could of course continue to expand Kawaii by adding adapters for other libraries (JAX-RS, JDBC, JAXB etc), but this will always be inferior to the libraries directly supporting a standardised MicroType.
Comments? Is microtyping an idea whose time has come in Java? Or it it's practical utility to small for it to ever catch on?