There is no real reason--you CAN work with hash maps instead of classes and sometimes it makes perfect sense. I tend to use hashes instead of classes for any of the following cases:
- All the values are of the same type (String)
- I never directly or individually reference the values in code.
- Tooling (like the stuff mentioned in the question)
The advantage that a map has over a class is that it's very easy to generically deal with a record at once. Once you put the values into a class you tend to reference the values by name making iterating over them more difficult.
The common alternative to Hashes in this problem space is Java Beans. Beans are a class with just properties and setters/getters for each property. Some people call these pojos, but pojos have no restrictions so if your class is intended to have just setters & getters call it a bean, not a pojo.
Both Beans and Maps are examples of Michael Brown's awesomely named "Dumb data", and that is an issue separate from the choice to use Maps or Beans.
There are two big problems with "Dumb Data" of both flavors, the first is that you need meta-data to know how to use it (Like how to bind it to a GUI control or a database column name or validations)
With maps you have to solve the meta-data problem with some external data file--XML or config files or something--you can intuit a lot of what you want to know (like assuming the column name in the DB matches the key name in the map) but I always seem to need some data.
With beans a neat solution has popped up--annotations.
An annotated bean can work like a map since you don't directly access variables by name, you just iterate over annotated fields. Annotated beans offer an improvement as well because the annotations to define all the meta-data in-line rather than requiring a separate description. This is how most of the ORM tools currently work--but you can create and use your own annotations pretty easily.
The second problem is harder though--it's that the data separated from code--you can't add business methods to a HashMap OR a Bean. This means they are not Objects, they are both more like data structures. They will break ALL your OO code/design if you use them directly--and they will break it in really infectious and hard-to-fix ways.
Normally with OO code you are not supposed to ask your object for data, you ask your object to do something with it's data for you. How on earth do you make this work with "dumb data"?
Lately my solution is that any "Dumb data" used directly by my code has a "Helper" class. As soon as I pull it from the DB (or wherever) I encapsulate it in it's helper class and never expose it to the rest of my codebase. any code that accesses the bean has to go through the helper class, any methods to read/modify/comprehend the data in the bean is put into the helper. Note that I'm not talking about getters and setters either--avoid those wherever possible--they are still cases of "Asking for the Object's data".
After adding this helper, it becomes obvious where to put a lot of code that you used to have distributed throughout the rest of your codebase (Often duplicated). Now you ask the helper class to do something, it interacts with the bean to do it.
I've even had separate wrappers implement business logic on the server and GUI logic/validations on the client each wrapping the same core "dumb data".
I think the most important distinction is not between Maps and Beans, it's between Classes and Dumb Data. Once your dumb data is encapsulated in a class, it's actual implementation (Bean, Hash, ???) is not even very interesting...