I am very annoyed by a common usage of Java Map it is not made for. Maps are here to represent a dictionary from keys to values, but it is often used to store unstructured content in place of a standard object.

Map<String, String> dog = person.getDog();
dog.put("name", "Foo");

The general reasoning is that a map makes it more extensible, but it makes it hard to validate the object right at modification time. In the example one would likely include some validate method inside the person object:

class Person {

  private Map<String, String> dog;

  /* ... */

  private void validateDog() {
    if (!dog.containsKey("name")) {
      throw new IllegalArgumentException("Dog name is invalid");
    }
  }
}

This prevents proper reusability of the dog object. And validating the class some time after the modification causes headaches when searching for the root cause of an issue because it is not shown by the stacktrace.

Then it encourages a violation of DRY because the underlying structure of the object is known by all its users. I also like to rely on Java type system to tell me about regressions as soon as possible, and here one would have to search everywhere in the project for the "name" string to see if it corresponds to a dog usage.

Generally keys in the map become public constants in some class, but this does not solve any of the issues with this pattern:

class Person {

  public static final String DOG_NAME = "name";

  /* ... */

}

To me, when keys in a map are predefined at compile time, there should be an object enforcing type checking. If at some point an unstructured map representation of the object is required, the object can internally use a map and expose it through a Map<String, ?> asMap() method. Clients would not know about the usage of a map and event less about key names.

class Dog {

  private Map<String, String> data;

  public void setName(String name) {
    data.put("name", name);
  }

  /* ... */

  public Map<String, String> asMap() {
    return Collections.unmodifiableMap(data);
  }
}

One could also make the API fluent but I’m not a big fan of it.