I am new to Java and have written a custom matcher to compare two maps because the error message from either JUnit or Hamcrest is not detailed enough. The code I wrote works, but I welcome critiques or comments to make it better.One question:Right now, I hard-coded my map to <String, String> but would like to be more generic. My first attempt was to use <Object, Object>, which resulted in a compiler error.Here is my matcher:package net.southeastwind.test;import java.util.HashMap;import java.util.HashSet;import java.util.Map;import java.util.Set;import org.hamcrest.Description;import org.hamcrest.TypeSafeDiagnosingMatcher;public class MapEquivalent extends TypeSafeDiagnosingMatcher<Map<String, String>> {    private Map<String, String> m_expected;    public MapEquivalent(Map<String, String> expected) {        m_expected = expected;    }    public static MapEquivalent mapEquivalent(Map<String, String> expected) {        return new MapEquivalent(expected);    }    @Override    public void describeTo(Description description) {        description.appendText(Maps are equivalent);    }    @Override    protected boolean matchesSafely(Map<String, String> actual, Description description) {        boolean matched = true;        Set<String> keys = new HashSet<>();        keys.addAll(actual.keySet());        keys.addAll(m_expected.keySet());        for (String key: keys) {            String expectedValue = m_expected.get(key);            String actualValue = actual.get(key);            if (null == expectedValue) {                matched = false;                description                    .appendText(\n\t\tOnly in actual: {).appendValue(key)                    .appendText(: ).appendValue(actualValue).appendText(});            } else if (null == actualValue) {                matched = false;                description                    .appendText(\n\t\tOnly in expected: {).appendValue(key)                    .appendText(: ).appendValue(expectedValue).appendText(});            } else if (!actualValue.equals(expectedValue)) {                matched = false;                description                    .appendText(\n\t\tValues differ: )                    .appendText(actual={).appendValue(key).appendText(: ).appendValue(actualValue).appendText(})                    .appendText(, expected={).appendValue(key).appendText(: ).appendValue(expectedValue).appendText(});            }        }        return matched;    }}Sample test and output:@Testpublic void compareMaps() {    Map<String, String> expected = new HashMap<String, String>() {{        put(alias, haiv);        put(uid, 501);        put(admin, no);    }};    Map<String, String> actual = new HashMap<String, String>() {{        put(alias, haiv);        put(uid, 502);        put(shell, bash);    }};    assertThat(Error 6a3429f7, actual, is(mapEquivalent(expected)));    // CustomMatchersTest > compareMaps FAILED    //     java.lang.AssertionError: Error 6a3429f7    //     Expected: Maps are equivalent    //          but:    //                 Values differ: actual={uid: 502}, expected={uid: 501}    //                 Only in actual: {shell: bash}    //                 Only in expected: {admin: no}}
Custom Hamcrest matcher to compare maps
java;junit