I don’t like dynamically-typed languages - scripting languages in other terms. Of course, I’ve to live with Javascript because it’s so ubiquitous on the web, but given the chance, I’d switch to TypeScript in an instant. I was amazed by a demo of the Griffon GUI framework, and have read some pretty good stuff about the Spock testing framework, but I barely looked at them because they are based on Groovy, a scripting language (even if it offers optional enforcement of types via annotations). This is not some idiosyncrasy: static typing helps the compiler to find errors. For me, costs of creating and maintaining a safety net just to ensure type correctness instead of letting the compiler handle it overweights most possible benefits.
(I know this is a strong claim, and you’re welcome to challenge it if you’ve valid and factual arguments, but this is not the subject of this post.)
Even within statically-typed languages, not everything is unicorns and rainbows.
In Java, one of such gray area is the String
class.
Before Java 5 and its enum
feature, string constants were used as enumeration values.
It was a weak design point, as two different constants could hold the same value.
// Pretty unsafe
public static final String CHOICE1 = "choice";
public static final String CHOICE2 = "choice";
String string = ...;
switch (string) {
case CHOICE1: // do something
break;
case CHOICE2: // <- this will never get executed
break;
}
enum
finally set things right.
// Safe
public enum Choice {
CHOICE1, CHOICE2
}
Choice choice = ...;
switch (choice) {
case CHOICE1: // do something
break;
case CHOICE2: // do something else
break;
}
Java 5 finally brought safety. Yet, even in Java 8, some design flaw persist. One of them is found in annotations. Nothing world-threatening, but enough to make me curse every time I have to write the following:
@SupressWarnings("deprecation")
That’s the way one has to write it: annotation do not accept enum
types!
The type-safe way would have been the following:
public enum SupressWarrningsType {
Deprecation // And others
}
@SupressWarnings(SupressWarrningsType.Deprecation)
The downside of this approach is that only set enumeration values are accepted and they are set in stone. Using string, additional strings can be added at any time.
All is not lost, though. Even with the current state of things, one can benefit from type-safe annotations by just writing the following once per project (or in a company’s shared library):
@SuppressWarnings("deprecation")
public @interface SuppressWarningsDeprecation
And then simply use it like that:
@SuppressWarningsDeprecation
Note that only two element values are mandatory: unchecked
and deprecation
are required by the Java Language Specification.
Additional options are available, depending on the provider.
For Oracle, type javac -X
and look for the -Xlint:
option to list them.
For the laziest among us - including myself, I already created the library. The source code is hosted on Github and the binary on Bintray. It’s available under the friendly Apache License 2.0. Just add the following dependency:
<dependency>
<groupId>ch.frankel</groupId>
<artifactId>safe-annotations</artifactId>
<version>1.0.0</version>
</dependency>
Whether you decide to use it or bake your own, you should strive to use type-safety when possible - that includes annotations.