I agree -- I prefer to keep things open, too. Somehow I didn't even think about those when I wrote my last mail, in many ways 'final' is evil just for having too many meanings. I think it's the cult of having less keywords that C++ created -- was it 'virtual' or 'const' that won the price for the most meanings there?
Peter On Sun, 2009-02-15 at 07:28 +0100, Alexander Snaps wrote: > I don't think a class should be marked final except if there is good > reason for doing so... > As for the rest I usually don't see much value in doing it. Though if > the project adopts it as a coding standard, why not. I'll just code > consistent with the style. Though it doesn't add much value IMHO. > Alex > > On 2/15/09, Reinier Zwitserloot <[email protected]> wrote: > > > > And when we need to make another modification, we.... rename the > > parameter to 'reallyOldConfigKey'? > > > > Peter: Me too, I spread final all over the place. on classes, on > > attributes, on methods. > > > > The only place where I rarely use it is for method locals :P > > > > While immutability would be great, what's really the key modifier is > > 'Side-Effect-Free'. Immutability is not well defined in practice. SEF > > is mostly well defined in practice (with exceptions where you really > > want to force it for e.g. logging, which seems like a side-effect but > > ought to be considered SEF). For example: > > > > Your class has all-final fields, and each field's type is neccessarily > > immutable (primitive, or a final class that is itself immutable). > > There is a static map (irrelevant for object's mutable/immutable > > state) that binds instances of this class to a String. The object uses > > this to sneakily be fake mutable. In fact, is an instance of this > > class an immutable in the first place? > > > > What about something like java.io.File, which is immutable (final and > > all its internal state never changes), but you can call things that > > mutate stuff on it, stuff that is generally considered to be part of > > the state of files, such as deleting them, creating them, etcetera. > > > > > > Side-Effect-Free avoids these hairy issues. You do need ways to say: I > > know this LOOKS like a side-effect, but consider it SEF. For example, > > logging. It would be completely unusable if the act of logging forces > > you to declare a method call SEF. > > > > You can also define an object as utterly safe and entirely memoizable > > if it contains only SEF methods and all its fields are final, and of > > safe types. This is a very useful distinction. > > > > > > APT processing might actually allow you to get somewhere with this, > > with annotations that allow you to specify intent (e.g.: This method > > is supposed to be SEF, so if it isn't, warn or error), and some > > serious class introspection. It would definitely help if you cached > > the SEF state of core java library calls (e.g. cache that after some > > analysis, you determine that calling string.toLowerCase() is > > harmless). > > > > > > On Feb 14, 7:43 pm, Robert Fischer <[email protected]> > > wrote: > >> You're right -- I don't like the academic discussion of "final", because > >> it's in practicalities > >> where "final" really shows its power. > >> > >> Comments intermingled below. > >> > >> > >> > >> Reinier Zwitserloot wrote: > >> > If we allow modification in the pre-amble, this is easy, bordering on > >> > the trivial (lines //V2 have been added/modified in the update): > >> > >> > public List<ConfigDirectives> readConfig(String configKey) { > >> > configKey = translateOldKeyNames(configKey); //V2 > >> > >> > try { > >> > Logger.get(this.getClas()).log("Request for config file: " + > >> > configKey); > >> > return readConfigDirectivesFromDisk(new FileInputStream > >> > ("configfiles/" + configKey + ".cfg")); > >> > } catch ( FileNotFoundException e ) { > >> > return Collections.emptyList(); > >> > } > >> > } > >> > >> > The one doing the editing really could get away with not even looking > >> > at what the actual body of the method does. Nice. > >> > >> > Now lets try with your rule: > >> > >> > Urgh. We can't. > >> > >> Really? What about this? > >> > >> public List<ConfigDirectives> readConfig(final String oldConfigKey) { > >> final String configKey = translateOldKeyNames(configKey); //V2.1 > >> > >> try { > >> Logger.get(this.getClas()).log("Request for config file: " + > >> configKey); > >> return readConfigDirectivesFromDisk(new FileInputStream > >> ("configfiles/" + configKey + ".cfg")); > >> } catch ( FileNotFoundException e ) { > >> return Collections.emptyList(); > >> } > >> > >> } > >> > >> That's a simple, easily makable, and readable change. In fact, now I've > >> got the original version > >> still kicking around (in case I want it later), and I've got the > >> translated version -- which is > >> really semantically different than "configKey" in the previous version -- > >> automatically being used. > >> > >> I'm pretty sure that change meets all your criteria. > >> > >> > The optimal way to solve this one in a functional view of the world is > >> > to first create a new variable (keyNormalized or something), and then > >> > UNDEFINE the old one. This way anyone that tries to use the original > >> > gets an error which should quickly lead to him or her coming up with > >> > the right solution (either use the normalized version or create a new > >> > paramRaw variable to make it abundantly clear what's happening). > >> > However, java doesn't support that. > >> > >> I just did it. Now, I didn't undefine the old variable, but I also don't > >> see how undefining the > >> variable is a part of the "the optimal way to solve this one in a > >> functional view of the world". > >> Sure, it's common to build up structures like: > >> > >> let foo = "f" in > >> let foo = foo + "o" in > >> let foo = foo + "o" in ... > >> > >> But that's more a hack around immutability than part of the functional > >> view of the world. Insofar > >> as that code can be conceived of as functional, this would be just as > >> functional: > >> > >> let f = "f" in > >> let fo = f + "o" in > >> let foo = fo + "o" in ... > >> > >> > >> > >> > Hence, my theory is: In anything but the most trivial of examples, > >> > inlining the transformation as you did in your myConcat method, is a > >> > bad thing more often than it being a good thing. Yes, there was a typo > >> > in your code (double 'left'), but I found that utterly unconvincing, > >> > because this typo is exactly as likely in your 'correct' method: > >> > >> > String myConcat(final String left, final String right) { > >> > if ( left == null ) return myConcat("", left); > >> > if ( right == null ) return myConcat(left, ""); > >> > return left + right; > >> > } > >> > >> We're now back into the academic, but since we're arguing what types of > >> errors are more likely, I'd > >> argue that an alarm bell is more likely to ring when you're putting a > >> variable named "left" into the > >> right side of an argument list as opposed to accidentally assigning the > >> wrong variable. So this > >> error case is less likely than the other. > >> > >> Even if it's "just as likely", you're still not demonstrating the grievous > >> harm (or whatever) that > >> "final" apparently does. > >> > >> ~~ Robert Fischer. > >> Grails Training http://GroovyMag.com/training > >> Smokejumper Consultinghttp://SmokejumperIT.com > >> Enfranchised Mind Bloghttp://EnfranchisedMind.com/blog > >> > >> Check out my book, "Grails Persistence with GORM and > >> GSQL"!http://www.smokejumperit.com/redirect.html > > > > > > > --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "The Java Posse" group. To post to this group, send email to [email protected] To unsubscribe from this group, send email to [email protected] For more options, visit this group at http://groups.google.com/group/javaposse?hl=en -~----------~----~----~----~------~----~------~--~---
