reactor / BlockHound

Java agent to detect blocking calls from non-blocking threads.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Don't use double-brace initialization in BlockHound.java

leventov opened this issue · comments

Double-brace initialization is considered an anti-patten.

Desired solution

Many uses of double-brace initialization in BlockHound.java could be replaced with Collections.singletonMap() call; the rest, with more mundane code like

map = new HashMap<>();
map.put(...)

Or a small static factory dedicated for this purpose.

@leventov

Thanks for submitting an issue.
I am aware that there are some cases where it may cause issues. However, unless one can prove our use of the double-brace initialisation harmful, I'm keen to keep it as is.

Just to give a bit of background and what motivated me to use them on a first place:

  1. singletonMap returns an immutable Map, while the maps we store are mutable and can be configured with the builder. One would need to wrap it with new HashMap<>(singletonMap(...)) and it creates additional noise
  2. singletonMap works only for one key/value pair. Adding more blocking methods, for example, would require a rewrite.
  3. We have a couple of imperative if/else that would be hard to use with a small static factory

Thus I would prefer to continue using the current approach. Feel free to re-open the issue if you still think there is anything harmful in it, e.g. it caused a memory leak or anything.

@bsideup why not use a helper method that accepts a supplier, consumer?
You could do it completely flexible with initMap or if you only use HashMap anyway use a more dedicated method hashMap.

import static org.assertj.core.api.Assertions.assertThat;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Supplier;

import org.junit.jupiter.api.Test;

public class TestMap {

    public static <K,V> Map<K,V> initMap(Supplier<Map<K,V>> creator, Consumer<Map<K,V>> initializer) {
        Map<K,V> map = creator.get();
        initializer.accept(map);
        return map;
    }

    public static <K,V> Map<K,V> hashMap(Consumer<Map<K,V>> initializer) {
        Map<K,V> map = new HashMap<>();
        initializer.accept(map);
        return map;
    }

    @Test
    public void InitMapWithMultiArgs(){
        Map<String,String> testMap = initMap(HashMap::new, map -> {
            map.put("a", "b");
            map.put("b", "c");
        });
        assertThat(testMap)
                .containsEntry("a", "b")
                .containsEntry("b", "c")
                .hasSize(2);
    }

    @Test
    public void HashMapWithMultiArgs(){
        Map<String,String> testMap = hashMap(map -> {
            map.put("a", "b");
            map.put("b", "c");
        });
        assertThat(testMap)
                .containsEntry("a", "b")
                .containsEntry("b", "c")
                .hasSize(2);
    }
}

@leonard84 because it works already, there are no issues with the current approach, and I simply don't want to make a change for the sake of a change?