When I used to roll my own OO, I would do this. I liked it because it (in combination with strictures) gave me compile time errors when I made a typo in a field name. With a similar error using a vanilla hash, you just keep getting undef or setting something that never gets read.
I didn't consider it opaque. All the fields in the object are nicely listed at the top of the module in a constant/enum statement.
I never roll my own OO anymore. Also, I realize now that low-level field access in enough places to make the above argument valid is probably a bad sign.