This is a proposal for a breaking change for how Groovy interacts with objects implementing the Map interface.
== Background == Groovy supports looking up the values of a map by converting property-style access `map.foo` to `map.get('foo')`. My understanding is that the purpose of this feature is to allow drop-in substitution of a map for a POJO. Currently, property-style access is always(?) routed to the Map's `get` method, regardless of any actual properties available on the object. This is the case even in static compilation where the variable's declared type implements a property matching the attempted access. This behavior leads to unexpected results and even heap pollution. As two examples, the `isEmpty()` method is defined for all collections and returns a primitive boolean. `list.empty` and `set.empty` correctly return true/false depending on the contents of the collection, but `map.empty` is interpreted as a request to `map.get('empty')`. This is entirely surprising, especially when it results in an NPE (when for some reason the null isn't as-booleaned). Additionally, some classes implementing Map also provide other ergonomics, such as Spring's HttpHeaders, which eventually implements `Map<String, List<String>>`. It has explicit computed properties for most of the common headers, with such property types as URI (Location) and long (Last-Modified). Groovy, however, interprets `headers.location` as a call to `headers.get('location')` and returns a `List<String>` instead of `headers.getLocation()`. This feature provides novel and evening-occupying results (particularly as Eclipse's hover and autocomplete suggest the expected behavior!). == Proposal == Starting in Groovy 4, prioritize explicitly defined properties for resolution ahead of `Map#get`. If some object implementing Map actually defines properties, then it seems to me a reasonable guess that that support is based on the intended/expected usage patterns, and that users employing that object would expect to have it available. In the case where clients specifically want to use a lookup, get and `[]` are easily available. -- Christopher Smith