How to change Indentation in Checkstyle for static Maps?

59 Views Asked by At

When I apply default google-checkstyle.xml format, the following will give a warning:

Map<String, Object> payment = new LinkedHashMap<>() {{
    put("customer_number", "123456");
}};

'block' child has incorrect indentation level 12, expected level should be one of the following: 16, 20. (169:13) [Indentation]

This is because checkstyle expects the following format:

Map<String, Object> payment = new LinkedHashMap<>() {{
        put("customer_number", "123456");
    }};

But I'd prefer my custom format. How could I change this in checkstyle? The error comes from definition:

<module name="Indentation">
    <property name="basicOffset" value="4"/>
    ....
</module>

But I cannot change this because the basicOffset also applies to all other lines.

Question: is it possible to somehow exclude static initialized Maps with double-brackets from this check? Like the new LinkedHashMap<>() {{.

2

There are 2 best solutions below

1
Rob Spoor On

If you don't care about the order, simply use Map.of like Joachim Sauer already suggested in his comment.

If you need insertion order, you can use the following instead of the anonymous sub class:

Map<String, Object> payment = List.of(
        // add as many entries as necessary
        entry("customer_number", "123456"))
        .stream()
        .collect(Collectors.toMap(
                Map.Entry::getKey,
                Map.Entry::getValue,
                (v1, v2) -> v1, // unused because the map keys will be unique
                LinkedHashMap::new))

This does look weird, using a List<Map.Entry<String, Object>> as intermediate object, and the toMap that requires a merge function that's never used, but it gets the job done.

0
k314159 On

It's unfortunate that there isn't already a neat way to create a Map with specified iteration order (Map.of doesn't specify it).

Unit tests are possibly the only place where the so-called "double-brace initialization" is justifiable:

  • You want the test code to be as concise as possible; DBI gives you that.
  • You don't care about efficiency, as it's only a test.
  • You don't care about creating loads of anonymous classes and the resulting *.class files because they won't be in the production jar file.

In all other situations, the general advice against using this anti-pattern stands. But if you still want the code to be readable, you can use a set of helper functions:

public static <K, V> Map<K, V> linkedHashMapOf(
        K key1, V value1,
        K key2, V value2,
        ...) {
    var map = new LinkedHashMap<K, V>();
    map.put(key1, value1);
    ...
    return map;
}

(Just like the Java API's Map.of set of functions.)

If you then use this in your JUnit test code, CheckStyle will be happy with the way you indent the arguments.

However, if the reason you're trying to create a LinkedHashMap in a JUnit test is to assert that a returned map contains the expected entries in the expected order, then you don't have to create a LinkedHashMap at all. Use your favourite assertion library's facilities instead. For example, with AssertJ, you can use

assertThat(actualMap).containsExactly(
        entry(a, b),
        entry(c, d)
);