What approximation would you choose to reflect your view then?
I don't know. My Java is too rusty and I'm not inclined to dig into it right now.
About "final" in Pike, I wasn't aware of that. Using "final constant" seems to solve half the problem (the larger half). It doesn't make it possible to create a different constant with the same name in a subclass though.
No, but "local" does.
Why do we need something which is "almost like a function" anyway?
Because we support access to non-functions in classes in Pike. If we'd think functions are good enough to access constant values then we'd reasonably also apply the same approach to variables and therefore force all variables to be private, or at least protected. From a OO-theoretical point of view that'd make the language more clean.
Why not just use a function? To save some typing?
Maybe, to a small extent. More importantly it saves unnecessary function calls, and it's more convenient when there is a substantial risk that the identifier doesn't exist at all. That's why Error.Generic uses constants:
class MyError { inherit Error.Generic; constant error_name = "MyError"; // ... }
mixed err = catch (blabla()); if (objectp (err) && err->error_name == "MyError") my_error_handling (err); else throw (err);
If we'd be forced to use functions here then the error test would become even clumsier:
if (objectp (err) && functionp (err->error_name) && err->error_name() == "MyError") ...