1. Syntactic sugar

Programmers love when some complex code or logic can be written in a couple of lines, making code compact and readable. And the creators of programming languages sometimes help with this.

A slick language feature that lets you take a shortcut (write less code) is called syntactic sugar. But, to be honest, there is very little of it in Java.

Java's creators did everything they could to eliminate any redundancy in Java. If C++ lets you do something 20 ways, then Java lets you do it only one way.

But neither Java programmers nor Java's creators liked the lack of freedom. And sometimes sugar makes life easier for ordinary folks like you and me.

By the way, you've already encountered some syntactic sugar: autoboxing and unboxing. Let's compare:

Long code Compact code
Integer a = new Integer(5);
int b = a.intValue();
Integer a = 5;
int b = a;
int b = 5;
Integer c = new Integer(b);
int b = 5;
Integer c = b;
Integer a = new Integer(1);
int b = 1;
if (a.intValue() == b)
{
   ...
}
Integer a = 1;
int b = 1;
if (a == b)
{
   ...
}

Instead of the long code like on the left, you can write the more compact code on the right. And intelligent Java compiler will generate the verbose version of the code based on the short version of the code. This is exactly what syntactic sugar is.


2. Inference of a variable's type: the var keyword

In Java 11, the compiler became even smarter and can now determine the type of a declared variable based on the type of the value assigned to it. In code, it looks like this:

var name = value;

Where name is the name of a new variable, value is its initial value, and var is a keyword used to declare the variable. The type of the name variable will be the same as the type of the value assigned to it.

Examples:

How we see the code What the compiler sees
var i = 1;
int i = 1;
var s = "Hello";
String s = "Hello";
var console = new Scanner(System.in);
Scanner console = new Scanner(System.in);
var list = new ArrayList<String>();
ArrayList<String> list = new ArrayList<String>();
var data = new int[]{1, 2, 3};
int[] data = new int[]{1, 2, 3};

The compiler itself determines, or infers, the variable's type based on the value assigned to it.

Programmers debated hotly over whether to add such a feature to the language. Many people feared that var would be abused and that code readability would suffer as a result.

There is a grain of truth to this, so it is best to use var where it increases the readability of the code. For example, these in two cases:

Case 1: Looking at the value assigned to the variable, the variable's type is immediately clear

Code Explanation
var stream = url.getInputStream();
The variable is an InputStream
var name = person.getFullName();
The variable is a String

In these cases, you shouldn't use var. Well, what is the variable's type?

Code Explanation
var result = task.execute();
It's difficult to determine the variable's type
var status = person.getStatus();
It's difficult to determine the variable's type

Case 2: The variable's type is not important for understanding the code

Code often has no need to call methods on a variable, e.g. when a variable is simply used to temporarily store something. In this case, using var definitely does not reduce the readability of the code:

Long code Compact code
var data = stream.getMetaData();
storage.save(data)
We got metadata from the stream stream and saved it in the storage repository. The data variable's specific type is not important.

The golden mean

Now I'll give three ways to write the same code. Using var would be the best option.

Code Note
dest.writeHeaderInfo(src.getFileMetaInfo());
Too compact
var headerInfo = src.getFileMetaInfo();
dest.writeHeaderInfo(headerInfo);
Just right
FileMetaInfo headerInfo = src.getFileMetaInfo();
dest.writeHeaderInfo(headerInfo);
Too detailed

Moving from the version with 1 line to the version on 2 lines, we made the code a little more readable by using a variable name (headerInfo). Now it's clear that the method returns not just meta information, but header information.

The third version is overly verbose. The fact that headerInfo is a FileMetaInfo is already fairly clear from the getFileMetaInfo() method. The purpose of the meta information is much more interesting.



3. Omitting the type with the diamond operator: <>

Even before the var operator appeared, there were attempts to teach the compiler how to infer collection types. You'll agree that this notation looks a little redundant:

ArrayList<String> list = new ArrayList<String>();

Starting from the seventh version of Java, when writing a collection type, you could omit the type of the collection elements if it was specified when declaring a variable. In other words, the code above can be written in a slightly abbreviated form:

ArrayList<String> list = new ArrayList<>();

As you can see, you no longer need to write String a second time. Not as cool as with the var operator, but it seemed like progress at the time.

The empty angle brackets in the collection type were called the diamond operator, since the two angle brackets vaguely resemble a diamond.

It's undesirable to use the var keyword and the diamond operator at the same time:

var list = new ArrayList<>();

There is no information at all about the type of the elements stored in the collection, and the collection type will be ArrayList<Object>.



4. Double curly braces

Remember quick array initialization?

We just listed values in curly braces, like this:

Examples
int[] data = new int[] {1, 2, 3, 4, 5, 6, 7};
int[] data = {1, 2, 3, 4, 5, 6, 7};

Java's creators loved the idea of using curly braces to simplify writing elements of an array. But what about collections?

Java's creators had enough creative thinking for collections as well, allowing them to use a trick with double curly braces.

With sugar Without sugar
var list = new ArrayList<String>()
{{
   add("Hello");
   add("How's");
   add("Life?");
}};
var list = new ArrayList<String>();

list.add("Hello");
list.add("How's");
list.add("Life?");

If the compiler encounters code like in the example on the left, then it converts it to the code on the right.

The code doesn't become a lot more compact. The savings here are fairly insignificant: you don't have to write list every time. This can be helpful if the variable name is very long.

But if you come across code like this in a project, don't be surprised 🙂