2¢ in Java type system enhancement


  • JSR 269: Pluggable Annotation Processing API
  • JSR 308: Annotations on Java Types
  • Checkers framework

Annotations were shown up first time in Java 5. They are providing meta information to compile and runtime. In Java 8 annotation support was extended, so now you can put annotation almost everywhere in your code.

This feature can help to declare constraints on existing types

Let’s try!

I expected that replacing the following code

void sendTopSecret(String secretMsg){


void sendTopSecret(@Encrypted String secretMsg){

will fail compilation because of sendTopSecret gets an unannotated string.

But the compilation passes with no errors

It’s happen becauss the compiler can verify the only syntax of annotation, not the semantics. It can’t distinguish between @Encrypted String secretMsg and String secretMsg

We need to implement processor to verify compiled code for every type of checkers we want to use

Build workflow will change from:

Source ↦ Compiler ↦ (if passes) Executable


Source ↦ Compiler ↦ (if passes) Executable ↦ (optional) Type checkers

This way of verification allows us to create a stronger type system without changing java lang itself Despite bit limited and more complex way these features can help a lot to produce more sophisticated and error-prone code

See example below

1) Create an annotation

package com.tikalk.simple.annotation.processing.demo;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

public @interface TestAnotation {}

And annotation processor

An annotation processor in our case will take compiled byte code as input and validate it .

package com.tikalk.simple.annotation.processing.demo;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class TestAnotationProcessor extends AbstractProcessor {
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
       // implement processor logic here
        return false;
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    public Set<String> getSupportedAnnotationTypes() {
        return new HashSet<>(Arrays.asList(TestAnotation.class.getName()));

2) Put file javax.annotation.processing.Processor in resources/META-INF/services/ And write down all your processors there

3) add compiler argument in pom.xml


All that we should do in client code is to add dependency and on the build time the processor will be invoked and do the verification logic

Good news: we have not implemented it by ourselves Checkers framework has an implementation of many kinds of checkers already. If you will want to you can implement custom checker by yourself it’s quite easy

See example checkers framework demo

Checkers framework integrates with build tools :

  • Maven
  • Gradle
  • Ant and with IDEs
  • IntelliJ
  • Eclipse
  • Netbeans

Or just use javac with “-processor”


This approach can guarantee the absence of errors and reduce the number of runtime checks Documentation and maintainability are improving as nice side effect.

Backdraws is time to set up the specification and write types. Also, it produces false positive (can be suppressed)

Senior Backend Developer

Backend Group

Thank you for your interest!

We will contact you as soon as possible.

Want to Know More?

Oops, something went wrong
Please try again or contact us by email at info@tikalk.com
Thank you for your interest!

We will contact you as soon as possible.

Let's talk

Oops, something went wrong
Please try again or contact us by email at info@tikalk.com