https://github.com/vinayalodha/elvis
Java Compiler plugin for Null safety
https://github.com/vinayalodha/elvis
java null-safety
Last synced: 6 months ago
JSON representation
Java Compiler plugin for Null safety
- Host: GitHub
- URL: https://github.com/vinayalodha/elvis
- Owner: vinayalodha
- License: apache-2.0
- Created: 2021-07-15T09:55:06.000Z (almost 5 years ago)
- Default Branch: main
- Last Pushed: 2025-09-30T09:05:43.000Z (9 months ago)
- Last Synced: 2025-09-30T11:22:30.828Z (9 months ago)
- Topics: java, null-safety
- Language: Java
- Homepage:
- Size: 426 KB
- Stars: 18
- Watchers: 2
- Forks: 2
- Open Issues: 15
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Elvis Operator for Java (Java Compiler plugin)
One way of writing null safe code in Java is using `Optional.ofNullable`, Even though I feel it is a better alternative
to writing `if` it gets repetitive and verbose.
I have written `Optional.ofNullable` countless number of times, and I don't want to write it anymore :)
This project aims to mitigate those problems using Java Compiler Plugin API.
## Usage
Consider you need to fetch `cardNumber` from `order` object, given order and its nested objects can be null, code
snippet would look something like below
### Java 7
public String getCardNumberFromOrder(Order order) {
if(order != null
&& order.getPayment() != null
&& order.getPayment().getCard() != null) {
return order.getPayment().getCard().getCardNumber();
}
return null;
}
### Java 8
Functional way with Java 8
public String getCardNumberFromOrder(Order order) {
return Optional.ofNullable(order)
.map(Order::getPayment)
.map(Payment::getCard)
.map(Card::getCardNumber)
.orElse(null);
}
### Elvis Plugin
public String getCardNumberFromOrder(Order order) {
@NullSafe
String cardNumber = order.getPayment().getCard().getCardNumber();
return cardNumber;
}
If you open class file for this code it will look something like below
public String getCardNumberFromOrder(Order order) {
return Optional.ofNullable(order)
.map(it -> it.getPayment())
.map(it -> it.getCard())
.map(it -> it.getCardNumber())
.orElse(null);
}
This is done using AST manipulation during compile time.
If you want to assign default value to cardNumber in above example, then code will look something like below
public String getCardNumberFromOrder(Order order) {
@NullSafe("1111222233334444")
String cardNumber = order.getPayment().getCard().getCardNumber();
return cardNumber;
}
For more examples please
refer [here](./plugin-test/src/test/java/io/github/vinayalodha/elvis/plugin/test/positive/AstTransformationTests.java)
and [here](./plugin-test/src/test/java/io/github/vinayalodha/elvis/plugin/test/positive/)
.
## Installation
### Maven configuration
You need to add `-Xplugin:ElvisPlugin` in compiler arguments
io.github.vinayalodha
elvis-plugin
1.0.3
provided
org.apache.maven.plugins
maven-compiler-plugin
3.8.1
11
11
-Xplugin:ElvisPlugin
For JDK 16 you need to set `true` and add additional args in tag. Please refer
to [JSR](https://openjdk.java.net/jeps/396)
-J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
-J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED
-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED
-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
### IntelliJ Idea
You need to add `-Xplugin:ElvisPlugin` in compiler arguments

## Current limitations
### Works only on local variable
Currently, @NotNull can only be used on local variable, this is due to fact Java annotation limit target type for
annotation to local variable
for example
public String getCardNumberFromOrder(Order order) {
@NullSafe
String cardNumber = order.getPayment().getCard().getCardNumber();
return cardNumber;
}
can not be written as
public String getCardNumberFromOrder(Order order) {
@NullSafe
return order.getPayment().getCard().getCardNumber(); // In Java you cant have annotation on return statement
}
### If you have classname in expression then you will get compilation error
For example, below code will give compilation error, given it contain classname(StringUtils) in expression
public String getCardNumberFromOrder(Order order) {
@NullSafe
String cardNumber = StringUtils.INSTANCE.trim();
return cardNumber;
}
Above code will throw compilation error saying `Unable to find Symbol StringUtils`
### If expression have Checked exception it will give compilation error
Consider example
public String getSomeStuff(SomeClass object) {
@NullSafe
String obj = object.thisMethodWillThrowIoException();
return obj;
}
Here after AST manipulation by this plugin code will become
public String getSomeStuff(SomeClass object) {
String obj = Optional.ofNullable(object)
.map(it -> it.thisMethodWillThrowIoException()) // In Java you can't have checked exception in lambda
.orElse(null);
return obj;
}
Given Java Stream won't allow checked exception in lambda, you will get compilation error saying
java: unreported exception java.io.IOException; must be caught or declared to be thrown
### Does not work with eclipse
Eclipse use its own compiler ECJ(Eclipse compiler for Java), and I could not find any way to pass
in `-Xplugin:ElvisPlugin`. I am still trying to find way where I can patch this.