From: Rhino on 22 May 2010 21:30 Tom Anderson <twic(a)urchin.earth.li> wrote in news:alpine.DEB.1.10.1005222205570.8422(a)urchin.earth.li: > On Sat, 22 May 2010, Rhino wrote: > >> Lew <noone(a)lewscanon.com> wrote in >> news:ht76kf$e1c$1(a)news.albasani.net: >> >>> Rhino wrote: >>>> ... getLocales(), which returns a TreeMap, and then displays the >>>> contents of the TreeMap on the console. >>> >>> It's mostly a good idea to declare variables as an interface type >>> rather than a concrete type. > > Rhino, read that sentence again. > >>> The rule of thumb is that you declare the variable with the loosest >>> possible type that has the behavioral contract you need. > > And that one. > > And again. > > Now proceed with the rest of this post. > >>> Thus, you probably want 'getLocales()' to return a 'SortedMap', as >>> someone suggested in another thread, unless there's something >>> specific about 'TreeMap' in particular that requires you use only >>> that type or a subtype thereof. >> >> For a second there, I had no idea what you were proposing. >> >> But I reviewed the Collection Classes in the Java Tutorial and I >> think I get it now. >> >> In all honesty, I hadn't even thought of SortedMap when I wrote the >> code again the other day. I just knew that I wanted the result to be >> in alphabetical order, not random the way that >> Locales.getAvailableLocales() provides them. > > You make that desire concrete by the type of the object holding the > locales - you want them to be sorted, so it's a SortedMap. > >> The article on the Map interface pointed out that TreeMap would >> assure that I had alphabetical order so I went with that. Now that >> you've reminded me about SortedMap, I can see the merit of it. It's >> not much different than TreeMap but it does give those additional >> features, like range operations. I don't see those as being >> _necessary_ for my humble little getLocales() method, which I'm >> really just writing for myself, but some future user of the class >> could conceivably benefit from those extra features. Or maybe _I_ >> will get a benefit from those features a little further down the >> road! > > You're on the wrong track here. The extra features are not what > SortedMap is about - it's fundamentally about that first paragraph in > its javadoc: > > A Map that further provides a total ordering on its keys. The map is > ordered according to the natural ordering of its keys [...] This > order is reflected when iterating over the sorted map's collection > views (returned by the entrySet, keySet and values methods). Several > additional operations are provided to take advantage of the > ordering. > > Yes, there are several additional operations. But the heart of the > matter is that the map has an order, which governs iteration over its > contents. > Sorry, I phrased my remarks poorly. I know the range handling and all that are more along the lines of bonus features; the order of the data is, of course, the main thing. >> I've modified the code to produced a SortedMap - just replaced all >> "Map" with "SortedMap", dead easy! - and reran my unit tests. >> Naturally, they still worked fine. >> >> Hmm. Did I do this right? >> >> I had this: >> >> ====================================================================== >> === public Map<String, Locale> getLocales() { >> >> Map<String, Locale> sortedLocales = new TreeMap<String, Locale>(); >> for (Locale oneLocale : Locale.getAvailableLocales()) { >> sortedLocales.put(oneLocale.getDisplayName(locale), oneLocale); >> } >> >> return sortedLocales; >> } >> ====================================================================== >> == >> >> and changed it to: >> >> ====================================================================== >> == public SortedMap<String, Locale> getLocales() { >> >> SortedMap<String, Locale> sortedLocales = new TreeMap<String, >> Locale>(); for (Locale oneLocale : Locale.getAvailableLocales()) { >> sortedLocales.put(oneLocale.getDisplayName(locale), oneLocale); >> } >> return sortedLocales; >> } >> ====================================================================== >> == > > Spot on. > >> The first line of the revised method looks a bit funny: >> SortedMap ... = TreeMap .... >> >> Did I do what you meant? > > Yes. You did *exactly* what Lew meant when he said: > > It's mostly a good idea to declare variables as an interface type > rather than a concrete type. > > SortedMap is an interface type - it says 'this map is sorted somehow'. > TreeMap is a concrete type - it says 'this map is implemented as a > red-black tree, which incidentally results in it being sorted'. > >> Sigh! I still struggle with understanding what I am doing >> sometimes.... I still don't REALLY understand the difference between >> these: >> >> - Map<String, Locale> sortedLocales = new TreeMap<String, Locale>(); > > It happens to be a TreeMap, but all the type declares is that it's a > map. > >> - SortedMap<String, Locale> sortedLocales = new TreeMap<String, >> Locale>(); > > It happens to be a TreeMap, but all the type declares is that it's a > sorted map. > >> - Map<String, Locale> sortedLocales = new SortedMap<String, >> Locale>(); > > Illegal. SortedMap is an interface. > I didn't actually try that; I just threw it out to illustrate how these things sort of all blur together in my brain.... >> Or, by the same token: >> >> - Calendar cal = Calendar.getInstance(); >> - Calendar cal = new GregorianCalendar(); >> - GregorianCalendar gcal = new GregorianCalendar(); > > Those last two examples are an exact analogue. The first one is a > little different - but a natural extension of the idea. > > The third version says "I care that this is object is specifically a > GregorianCalendar. I am going to do things with it which specifically > would not work with any other kind of calendar.". There are times when > you might want to say that, but you would strive to be more general - > in which case you would use the second version, which says "I care > that this object is a Calendar, but i don't care which kind of > calendar - it could be Gregorian, Islamic, Japanese Imperial, Darian > Jovian, etc". However, in the second form, although you will proceed > to write your code calendar-agnostically, you are actually hardcoding > the choice of GregorianCalendar there. You could change it later, but > you'd have to go through your code and change the constructions. In > the first form, you push the calendar-agnosticism even into the > creation itself - rather than explicitly using a GregorianCalendar, > you let Calendar decide what kind of calendar should be used. Right > now, that will presumably always be a GregorianCalendar, but if at > some point someone boots up your software on Ganymede (the moon, not > the Eclipse release), it should probably be Darian, and by leaving the > decision to Calendar.getInstance, you let that happen without having > to alter your code. You delegate the decision to someone in a better > position to make it. > > Now, in this particular case, there is a major caveat: different types > of Calendar are not interchangeable in the way that different types of > Map are, because they represent different ideas. If you're using this > Calendar object to handle dates which are definitively in Gregorian > (January, February and all that), then you need an actual > GregorianCalendar, and you should declare your variables accordingly. > To paraphrase Einstein, "everything should be made as generic as > possible, but no more so". > Thanks for clarifying the differences between the two groups of statements I listed above. You did quite a nice job and I feel that I have a little better grasp of it. But I'd be lying if I really felt I have a really solid handle on things. I feel like it should all be more obvious somehow and not require me to rack my brain each time this kind of thing comes up to try to understand just what the key distinction is between X and X'..... >> If I had to write an exam, clearly articulating the distinction >> between those and what the implications of each is, I'd surely make a >> hash of it.... > > That would be a real mistake. You should be making a tree. Have you > learned NOTHING? LOL!! Good one! > >> I can tell if I'm getting a compile error and I can tell if my code >> is doing what I want it to do but I still don't always understand WHY >> one thing is better than an other or what the real difference is >> between things that look very very similar..... > > The choice between Map and SortedMap here is not one of correctness. > It's one of design and style. Good design and style doesn't change the > way code runs, but it makes it easier to understand and modify. A huge > part of the business of software is understanding and modifying > existing code, and so good design and style are valuable. They're > investment for the future, rather than the here and now. > I'm fine with those general principles. I have done maintenance programming and I have been handed badly written programs that made me cringe just to look at them - as I'm sure we all have. I've always gone the extra mile to make my code just as good as I was capable of making it in every way that I could: make it work, make it clear, make it easy to read, etc. Thanks for your advice and your clear explanations, Tom! Much appreciated! -- Rhino
From: Rhino on 22 May 2010 21:30 Tom Anderson <twic(a)urchin.earth.li> wrote in news:alpine.DEB.1.10.1005222205570.8422(a)urchin.earth.li: > On Sat, 22 May 2010, Rhino wrote: > >> Lew <noone(a)lewscanon.com> wrote in >> news:ht76kf$e1c$1(a)news.albasani.net: >> >>> Rhino wrote: >>>> ... getLocales(), which returns a TreeMap, and then displays the >>>> contents of the TreeMap on the console. >>> >>> It's mostly a good idea to declare variables as an interface type >>> rather than a concrete type. > > Rhino, read that sentence again. > >>> The rule of thumb is that you declare the variable with the loosest >>> possible type that has the behavioral contract you need. > > And that one. > > And again. > > Now proceed with the rest of this post. > >>> Thus, you probably want 'getLocales()' to return a 'SortedMap', as >>> someone suggested in another thread, unless there's something >>> specific about 'TreeMap' in particular that requires you use only >>> that type or a subtype thereof. >> >> For a second there, I had no idea what you were proposing. >> >> But I reviewed the Collection Classes in the Java Tutorial and I >> think I get it now. >> >> In all honesty, I hadn't even thought of SortedMap when I wrote the >> code again the other day. I just knew that I wanted the result to be >> in alphabetical order, not random the way that >> Locales.getAvailableLocales() provides them. > > You make that desire concrete by the type of the object holding the > locales - you want them to be sorted, so it's a SortedMap. > >> The article on the Map interface pointed out that TreeMap would >> assure that I had alphabetical order so I went with that. Now that >> you've reminded me about SortedMap, I can see the merit of it. It's >> not much different than TreeMap but it does give those additional >> features, like range operations. I don't see those as being >> _necessary_ for my humble little getLocales() method, which I'm >> really just writing for myself, but some future user of the class >> could conceivably benefit from those extra features. Or maybe _I_ >> will get a benefit from those features a little further down the >> road! > > You're on the wrong track here. The extra features are not what > SortedMap is about - it's fundamentally about that first paragraph in > its javadoc: > > A Map that further provides a total ordering on its keys. The map is > ordered according to the natural ordering of its keys [...] This > order is reflected when iterating over the sorted map's collection > views (returned by the entrySet, keySet and values methods). Several > additional operations are provided to take advantage of the > ordering. > > Yes, there are several additional operations. But the heart of the > matter is that the map has an order, which governs iteration over its > contents. > Sorry, I phrased my remarks poorly. I know the range handling and all that are more along the lines of bonus features; the order of the data is, of course, the main thing. >> I've modified the code to produced a SortedMap - just replaced all >> "Map" with "SortedMap", dead easy! - and reran my unit tests. >> Naturally, they still worked fine. >> >> Hmm. Did I do this right? >> >> I had this: >> >> ====================================================================== >> === public Map<String, Locale> getLocales() { >> >> Map<String, Locale> sortedLocales = new TreeMap<String, Locale>(); >> for (Locale oneLocale : Locale.getAvailableLocales()) { >> sortedLocales.put(oneLocale.getDisplayName(locale), oneLocale); >> } >> >> return sortedLocales; >> } >> ====================================================================== >> == >> >> and changed it to: >> >> ====================================================================== >> == public SortedMap<String, Locale> getLocales() { >> >> SortedMap<String, Locale> sortedLocales = new TreeMap<String, >> Locale>(); for (Locale oneLocale : Locale.getAvailableLocales()) { >> sortedLocales.put(oneLocale.getDisplayName(locale), oneLocale); >> } >> return sortedLocales; >> } >> ====================================================================== >> == > > Spot on. > >> The first line of the revised method looks a bit funny: >> SortedMap ... = TreeMap .... >> >> Did I do what you meant? > > Yes. You did *exactly* what Lew meant when he said: > > It's mostly a good idea to declare variables as an interface type > rather than a concrete type. > > SortedMap is an interface type - it says 'this map is sorted somehow'. > TreeMap is a concrete type - it says 'this map is implemented as a > red-black tree, which incidentally results in it being sorted'. > >> Sigh! I still struggle with understanding what I am doing >> sometimes.... I still don't REALLY understand the difference between >> these: >> >> - Map<String, Locale> sortedLocales = new TreeMap<String, Locale>(); > > It happens to be a TreeMap, but all the type declares is that it's a > map. > >> - SortedMap<String, Locale> sortedLocales = new TreeMap<String, >> Locale>(); > > It happens to be a TreeMap, but all the type declares is that it's a > sorted map. > >> - Map<String, Locale> sortedLocales = new SortedMap<String, >> Locale>(); > > Illegal. SortedMap is an interface. > I didn't actually try that; I just threw it out to illustrate how these things sort of all blur together in my brain.... >> Or, by the same token: >> >> - Calendar cal = Calendar.getInstance(); >> - Calendar cal = new GregorianCalendar(); >> - GregorianCalendar gcal = new GregorianCalendar(); > > Those last two examples are an exact analogue. The first one is a > little different - but a natural extension of the idea. > > The third version says "I care that this is object is specifically a > GregorianCalendar. I am going to do things with it which specifically > would not work with any other kind of calendar.". There are times when > you might want to say that, but you would strive to be more general - > in which case you would use the second version, which says "I care > that this object is a Calendar, but i don't care which kind of > calendar - it could be Gregorian, Islamic, Japanese Imperial, Darian > Jovian, etc". However, in the second form, although you will proceed > to write your code calendar-agnostically, you are actually hardcoding > the choice of GregorianCalendar there. You could change it later, but > you'd have to go through your code and change the constructions. In > the first form, you push the calendar-agnosticism even into the > creation itself - rather than explicitly using a GregorianCalendar, > you let Calendar decide what kind of calendar should be used. Right > now, that will presumably always be a GregorianCalendar, but if at > some point someone boots up your software on Ganymede (the moon, not > the Eclipse release), it should probably be Darian, and by leaving the > decision to Calendar.getInstance, you let that happen without having > to alter your code. You delegate the decision to someone in a better > position to make it. > > Now, in this particular case, there is a major caveat: different types > of Calendar are not interchangeable in the way that different types of > Map are, because they represent different ideas. If you're using this > Calendar object to handle dates which are definitively in Gregorian > (January, February and all that), then you need an actual > GregorianCalendar, and you should declare your variables accordingly. > To paraphrase Einstein, "everything should be made as generic as > possible, but no more so". > Thanks for clarifying the differences between the two groups of statements I listed above. You did quite a nice job and I feel that I have a little better grasp of it. But I'd be lying if I really felt I have a really solid handle on things. I feel like it should all be more obvious somehow and not require me to rack my brain each time this kind of thing comes up to try to understand just what the key distinction is between X and X'..... >> If I had to write an exam, clearly articulating the distinction >> between those and what the implications of each is, I'd surely make a >> hash of it.... > > That would be a real mistake. You should be making a tree. Have you > learned NOTHING? LOL!! Good one! > >> I can tell if I'm getting a compile error and I can tell if my code >> is doing what I want it to do but I still don't always understand WHY >> one thing is better than an other or what the real difference is >> between things that look very very similar..... > > The choice between Map and SortedMap here is not one of correctness. > It's one of design and style. Good design and style doesn't change the > way code runs, but it makes it easier to understand and modify. A huge > part of the business of software is understanding and modifying > existing code, and so good design and style are valuable. They're > investment for the future, rather than the here and now. > I'm fine with those general principles. I have done maintenance programming and I have been handed badly written programs that made me cringe just to look at them - as I'm sure we all have. I've always gone the extra mile to make my code just as good as I was capable of making it in every way that I could: make it work, make it clear, make it easy to read, etc. Thanks for your advice and your clear explanations, Tom! Much appreciated! -- Rhino
From: Lew on 22 May 2010 21:37 On 05/22/2010 09:30 PM, > in > news:alpine.DEB.1.10.1005222205570.8422(a)urchin.earth.li: Rhino wrote: >>> - Map<String, Locale> sortedLocales = new SortedMap<String, >>> Locale>(); Tom Anderson wrote >> Illegal. SortedMap is an interface. Rhino wrote: > I didn't actually try that; Clearly not. > I just threw it out to illustrate how these things > sort of all blur together in my brain.... You were advised not to do that. Strongly. I strongly advise you, again!, to stick with SSCCEs until you stop making that kind of mistake. That you have not learned your lesson about posting uncompilable code is starting to make you look careless and lazy. (Naturally this does not apply to posts that intentionally illustrate code that does not compile, but such posts invariably include verbiage that indicates that the code is intentionally illegal.) -- Lew
From: Patricia Shanahan on 22 May 2010 23:14 Rhino wrote: .... > I probably tend to overbuild a lot of the time anyway, in the sense of > adding functionality that isn't all that likely to be used. My current > thread about the internationalization/localization is proof of that. I > tend to anticipate needs that others aren't sure will ever happen. Not > all the time of course; it's just a tendency, not an obsession ;-) .... I also get tempted to overbuild. When I started programming, there was some justification, because making design changes in a large program was difficult and risky. These days, it is so easy to refactor that there is generally little benefit to adding complexity that is not needed for immediate needs. The exception is designing APIs that will be used in code outside your control. Patricia
From: Lew on 22 May 2010 23:54
Lew wrote: >> Are you familiar with the difference between interfaces and classes? Rhino wrote: > I'm not sure how to answer that. I've read formal definitions of both and > have written both and gotten them to run. But even after all the time > I've spent writing Java - I started in 1997 but had a 4 year gap that > ended a few months back where I wrote no Java at all - I don't feel solid > on a lot of the theory. Maybe I'm expecting too much of myself but I feel > like I should know instantly and intuitively what the key differences are > between classes and interfaces and not have to wrestle with "should I use > an interface or class here". The Java tutorial describes the difference, but doesn't really address your question, so I'll take a stab. Interfaces are pure contract - they describe the signatures of the methods that class instances must implement, but may not contain any method implementation nor instance members. (It's also usually a bad idea to give them 'static' members.) Classes may contain some implementation, and must be completely implemented (if not 'abstract'). A class can implement an interface - that is, it is a subtype of the interface that fills in the missing implementation details. So you can have an interface: public interface Foo { public void fooishBehavior(); } Notice the semicolon instead of a method body - no curly braces in the method. It's a promise that there will be such a method in any class that implements the interface. You can have an implementing class: public class FooImpl implements Foo { public void fooishBehavior() { } } The class's claim that it 'implements Foo' requires it to implement the 'Foo' methods, or at least be an abstract class. We'll ignore abstract classes for you to study on your own. The interface is a type - this is the key. All subtype classes or interfaces are necessarily also of that type. 'extends' and 'implements' both express a subtype relationship, and a subtype thing /is-a/ thing of its parent types. (Inheritance expresses /is-a/; membership, a.k.a. composition, expresses /has-a/.) The key is to think in terms of types, not classes. Interfaces are pure expressions of type, and classes express a way for the type to do something actual. Different implementors of an interface can do quite different things, but they must do the things promised by the type they implement. Think of "type" as a contract - it shall have these behaviors! All implementors of 'Foo' above must implement (or pass on the responsibility abstractly to implement) that 'fooishBehavior()' method. How they do so is up to them. Variables have a type - that's what's important. When you use a variable of type 'Foo', all you really care about is to use its 'fooishBehavior()'. You should not (usually - always the disclaimer) care how the thing does its 'fooishBehavior()'. So you typically have a construct like Foo foo = new FooImpl(); Since everything of a subtype ('FooImpl') /is-a/ thing of the supertype ('Foo'), this is legal, and since 'Foo' is the type you care about, this is proper. Later you might refactor the code. Perhaps you discover that 'FooImpl#fooishBehavior()' is not thread safe but you need it to be. However, the logic of the program doesn't need to know that, only that 'foo' has the method, safe or not. So you change that initialization to: Foo foo = new ConcurrentFooImpl(); The rest of the program, knowing only that 'foo' is of type 'Foo', needs no rewrite. The hidden secret of the refactored 'foo' buys you thread safety, and the rest of the program needs no recompile. Magic! This came up in a project where I worked when we substituted something like Map <Foo, Bar> whatever = Collections.synchronizedMap( new HashMap <Foo, Bar> () ); with Map <Foo, Bar> whatever = new ConcurrentHashMap <Foo, Bar> (); and got a huge boost in the program's performance. -- Lew |