 |
java.net Forums
Book Clubs Archive > Effective Java Programming Language Guide
Joshua Bloch, author of Effective Java, casts a long shadow in the Java world. He has served as architect of the Core Java Platform Group and is best known as the author of the
Collections Framework. He was also a driving force behind many of the sweeping changes forthcoming in the JDK 1.5 (Tiger) release. In
Effective Java Bloch has condensed his rich body of expertise into 57 topics, each of which illustrates a specific Java coding best practice. The rare book that is simultaneously textbook and reference, Effective Java deserves a featured place on every Java Programmer's bookshelf.
Because Effective Java is "thread-safe" we're going to
dedicate one discussion thread to each of the items in the book.
Please post your comments on the relevant topic thread to maximize
coherence and avoid unnecessary context switching.
Discussion Moderator:
Ron Hitchens
Showing messages 1 through 250 of 285.
Next Page 
-
Asking tips
2004-08-27 05:05:56 celson
I'm a new entry in the programming world. I olny have a month studying Java. Could someone give me some information about methods and classes and some exercises?
-
Th...Th...Th...That's All Folks
2004-08-23 13:44:20 ronhitchens
We've come to the end of Effective Java by Joshua Bloch.
It's been a very interesting discussion and has generated far more
postings than any other Book Club Forum to date. My apologies for not
keeping the postings flowing at a steady rate -- work and family
commitments got in the way at a few points along the way. The deadline
for posting to this forum has been extended to Friday, August 26. I
encourage everyone to read back over the topic threads and add further
comments.
Was this discussion useful and interesting for you? Did the format
(one thread per item) work ok? Were the conversations easy to follow?
Too much material? Not enough? At the right level?
Finally, please join me in wishing Josh the best of luck in his new
job at Google.
-
Th...Th...Th...That's All Folks
2004-08-24 09:32:48 johnm
The deadline for posting to this forum has been extended to Friday, August 26.
It would be much nicer, IMHO, if the posting and discussion didn't have to get arbitrarily stopped at the end of this month.
I quite liked the one thread per item format as that gave some initial structure to the discussion.
I'm a glutton for punishment and would have liked to have seen more discussion by more people. One of the coolest things about an online forum such as this is the wide variety of perspectives that can be shared.
-
Th...Th...Th...That's All Folks
2004-08-23 19:35:16 sumitkishore
Now that it's ended, could you please provide the complete series in a format that is easier to follow? This was an informative series, but all the interleaving email chains was confusing as hell if you were trying to follow one of the items. Please - links to all the items individually, with their relevant discussion.
-
Th...Th...Th...That's All Folks
2004-08-23 17:38:30 hlovatt
Off topic, but does anyone know what Joshua Bloch will be doing at Google?
If his work at Sun is anything to go by it will be good stuff. Obviously Effective Java is a well liked book and I like the Collections API and I think the decision to provide optional methods in that API was brave and original and has worked well.
-
Item 57: Provide a readResolve method when necessary
2004-08-23 13:39:10 ronhitchens
The readResolve() method lets you substitute a different object than the one created by the deserialization process. Use of this method has been advocated for quite a while with Singletons and RMI. Have you used it in any other context? Were you aware that you could use it to protect against tampering with serialization streams? To achieve the same defensive goals of Item 56 by invoking real, non-no-arg class constructors? Are you aware of the cases where readResolve() should not be used, as regards inheritance?
-
Item 57: Provide a readResolve method when necessary
2004-08-23 17:35:54 hlovatt
The readResolve method is great but it is tricky to use with inheritance, pity there isn't a simpler mechanism.
-
Item 56: Write readObject methods defensively
2004-08-23 13:38:15 ronhitchens
After reading this item it's a little distressing to realize how easily serialized objects can be compromised. Do you think of readObject() as a constructor, with the same responsibilities for validating invariants? Did you realize how easy it is to tamper with the byte stream? Did you know about the trick for gaining access to private fields inside an immutable object? Do you make defensive copies of fields when appropriate? Do you know all the cases where it's appropriate?
-
Item 55: Consider using a custom serialized form
2004-08-23 13:35:38 ronhitchens
The serialized form of a class is a part of its public API, so it pays to get it right the first time. Do you always (or have you ever) designed a custom serialization for a serializable class? Do you think about the logical vs physical structure of a class? How about the space/time savings that are possible with a custom representation? Do you document classes and methods properly with @serial and @serialdata Javadoc tags?
-
Item 54: Implement Serializable judiciously
2004-08-23 13:34:52 ronhitchens
This one probably has the most bold type of any of item -- it warns of all manner of unpleasant side effects of making a class serializable. Were you aware of all these issues (brittle binary encoding, security, testing, impact on inheritance, constructor constraints)? When making classes serializable do you consider the costs of maintaining and evolving that class?
-
Item 54: Implement Serializable judiciously
2004-08-24 11:19:31 c_jinx
A good discussion of serialization issues can be found in 'Component Development for the Java Platform' by S. Halloway. Which by the way, would make a good candidate for a future book club forum
Off-topic: I wish java.net would publish the list of to be discussed books three months in advance. People should have time to prepare properly.
-
Item 54: Implement Serializable judiciously
2004-08-24 06:52:13 ljnelson
Yet another reminder of why testing during development is a good thing: it is now obvious to me, but was not at the time, that if you make a class Serializable then none of its instance variables can be made final.
-
Item 53: Avoid thread groups
2004-08-19 14:23:42 ronhitchens
Bloch sums it up: "To summarize, thread groups don't provide much in the way of useful functionality, and much of the functionality they do provide is flawed". Have you ever had a legitimate use for thread groups? What about the case of ThreadGroup.uncaughtException() that's invoked whenever a thread in the group throws an uncaught exception?
-
Item 52: Document thread safety
2004-08-19 14:22:32 ronhitchens
Five types of thread safety: immutable, thread-safe, conditionally thread-safe, thread-compatible and thread-hostile. Which bucket does your class fall into? Did you document it? Does making all your methods synchronized automatically make your class immune from threading problems? Why or why not?
-
Item 52: Document thread safety
2004-08-24 11:49:17 threadweaver
I use a marker interface called Flyweight to signify that an object implements the Flyweight[GoF] design pattern. It might also be helpful to have a method in an interface that returns the level of thread safety for a class.
-
Item 52: Document thread safety
2004-08-23 17:33:49 hlovatt
I must admit that I only document thread safe classes and take it as read that if it doesn't say it is thread safe then it isn't.
-
Item 51: Don't depend on the thread scheduler
2004-08-19 14:22:02 ronhitchens
Thread scheduling is, by definition, nondeterministic. Have you ever depended on busy-waits for thread rendezvous? Have you used Thread.yield() when Thread.sleep() or Object.wait() would have been a better choice? Ever relied on thread priorities to make things happen in a specific order?
-
Item 49: Avoid excessive synchronization
2004-08-18 17:36:51 ronhitchens
This is the flip-side of item 48 and there are basically two
concerns here: be careful about calling alien code (code you
don't know or trust) from within a synchronized block, and holding
locks when you don't need to. What are the dangers of invoking alien
code while holding one or more locks? Is this ever a good idea? What
are the problems that can stem from holding locks unnecessarily? Can
locks be too fine grained?
-
Item 49: Avoid excessive synchronization
2004-08-20 04:47:35 javakoe
Try to avoid blocking (like IO) code while holding a lock. I have had problems with servlets holding locks while writing output. When one browser stalled everything kept waiting for the lock.
-
Item 48: Synchronize access to shared mutable data
2004-08-18 10:35:22 ronhitchens
This is a very important item for several reasons. I'm sure most
Java programmers understand the mutual exclusion aspects of
synchronization, but far fewer understand the issues relating to
visibility of data values between threads. Synchronization boundaries
not only arbitrate locks, they also control inter-thread memory
coherence.
Do you fully understand the (often non-obvious) behavior specified
by the Java Memory Model? Have you ever used flags for inter-thread
notification (like the thread termination example) without doing the
proper synchronization? How about the double-check lazy initialization
idiom? Did you (like most of us) "invent" this clever scheme to avoid
synchronization overhead? How many latent bugs are lurking out there
in the world that are a result of incomplete comprehension of
synchronization? Do the JDK 1.5 changes to the memory model affect any
of the advice given in this item?
-
Item 47: Don't ignore exceptions
2004-08-16 12:49:31 ronhitchens
Empty catch blocks are ticking time bombs. Do you ever silently swallow exceptions? How many times have you run across this in other peoples code? Are catch blocks that just print a stack trace and keep going any better? Do IDE tools that auto-generate empty try/catch blocks exacerbate this problem?
-
Item 47: Don't ignore exceptions
2004-08-16 11:19:21 hlovatt
This is not always what you want, there are circumstances when program termination with an error message is the best that you can do. EG if a compiler can't access a file because of a hardware problem there is little point continuing.
For a GUI program I find a top level catch clause that reports the problem to the user very useful. This catch clause combines well with 46 (Atomic Failure), i.e. if one aspect of the program is having a problem the user can try something else. EG if they are having a problem saving a file locally then they can try saving to a remote machine instead.
The GUI example s a case were an unchecked exception is more useful than a checked because you don't have to list in the throws clause the exception, however I do typically list the unchecked exception in the throws clause and also in the Javadoc.
Another characteristic of the GUI example with a top level catch is that it is useful to wrap the exceptions you want to pass to the top level catch in an exception specific to the application, item 43.
-
Item 47: Don't ignore exceptions
2004-08-17 17:41:17 jwenting
and sometimes (in admittedly rare cases) you really don't care about the exception at all.
For example I've one application where I always try to create a specific object and have it perform an action (which object is created depends on the command passed in by the user) (command pattern).
As I took over this application from someone else who had created the whole thing as a monolithic block of code (and I've not yet gotten around to completely refactoring it all) there are a few commands for which no corresponding class exists.
Rather than keep a list of all the commands to ignore the command pattern system for, I just let it fail and ignore the exceptions (while having the catchblock contain a clear comment as to the exact why of course).
As this is an internal API only and I know that this is the only way the specific exceptions here will be thrown there's no danger (subsequent lines would fail when the command can't be processed because there's no instance created anyway in a clear way which would happen only if for some reason a classfile had failed to deploy).
-
Item 46: Strive for failure atomicity
2004-08-16 12:48:36 ronhitchens
Failure atomicity means that if a object throws an exception it should be in a well-defined, usable state afterwards. When you throw an exception, do you think about what will happen on the next invocation? Do you structure your code to check for everything that can go wrong before changing anything? If that's not practical, do you properly rollback? What do you do when the situation is hopeless and the object should be abandoned?
-
Item 46: Strive for failure atomicity
2004-08-16 11:08:17 hlovatt
I think that when the situation arises that the user of the API can do little about the best answer is to throw an un-checked exception, see item 40.
I find that good API designs, designs that are easy to use, often have the characteristic of atomic failure and bad designs are non-atomic. I thinc atomic failure goes with atomic methods, another good feature of an API design.
-
Item 45: Include failure-capture information in detail messages
2004-08-16 05:47:06 ronhitchens
An exception that tells you nothing about why it occurred is almost worthless (throw new Exception("Failed");). When throwing an exception do you include pertinent information about what went wrong? Do you think about how your detail message communicates to the caller of your method? For checked exceptions, do you provide accessors for important data values in addition to embedding them in the message string?
-
Item 45: Include failure-capture information in detail messages
2004-08-16 10:57:19 hlovatt
I find deriving a meaningfully named exception from a standard exception a good way to document what went wrong
-
Item 45: Include failure-capture information in detail messages
2004-08-17 09:58:51 jimothy
That's a good start, but you can go further by including specifics. For example, FileNotFoundException tells you what file was not found. An exception thrown by a parser (e.g., SAXParseException) can tell you the line and column number of the failure. We don't have it today, but it would be great if a subclass of SQLException told you what table or column doesn't exist.
If you're processing a collection in a loop and you need to throw an exception, you can do yourself a favor by including information about the collection element that was being processed at the time. The line number from the stack trace alone doesn't help, because that line of code is executed multiple times.
Help the exception help you pinpoint errors.
-
Item 44: Document all exceptions thrown by each method
2004-08-16 05:46:06 ronhitchens
We should keep in mind that the "throws" clause in a method header also serves as documentation. Do you enumerate all the checked exceptions a method throws, even if some have a common superclass? Do you ever use "throws Exception" to reduce the clutter? If a method throws so many different exceptions that it's cluttered, is that a red flag?
-
Item 44: Document all exceptions thrown by each method
2004-08-16 17:55:50 hlovatt
Very sound advice, checked and unchecked should be documented both in the throws clause and in the Javadoc.
-
Item 43: Throw exceptions appropriate to the abstraction
2004-08-16 05:44:13 ronhitchens
It's important not to pollute interfaces with implementation details, which are subject to change. Do you always translate exceptions thrown from lower level code? What are the problems with passing through exceptions without translating them? Do you always chain exceptions by passing the root cause exception to the wrapping exception? Is it ever appropriate not to chain exceptions? What problems result if you don't chain?
-
Item 43: Throw exceptions appropriate to the abstraction
2004-08-16 10:50:26 hlovatt
This advice contradics 42, use standard exceptions, there is some support for this approach in the discussions for 42. I don't find I do this that often, I favour deriving from a standard exception, i.e. I follow 42, more than throwing an exception specific to an API or method.
That is not to say I never throw an exception specific to an API, the advice is sound for an API that is likely to change the exceptions it throws as the API develops or you need to catogorize the exceptions under a standard heading so that they can be caught together.
I find wrapping the standard exceptions in the API specific exception a useful technique when throwing an API specific exception.
-
Item 43: Throw exceptions appropriate to the abstraction
2004-08-24 09:56:58 johnm
IIRC, Item 42 is talking about those specific unchecked exceptions.
One of the key problems with the whole world of Java's exceptions is that they are in sort of a limbo between really being (considered) part of the typing system and being (considered) an independent, execution bypass mechanism. Given this wide, amorphous nature, it's not hard to see how we've gotten to the chaos that we're currently in (with such things as the checked vs. unchecked war, etc.).
-
Item 42: Favor the use of standard exceptions
2004-08-12 12:57:46 ronhitchens
Six general purpose, reusable unchecked exceptions are listed in the table on page 177. Have you ever created your own version of one or more of these? This item encourages you to extend these exceptions when needed. Is that a good idea? For situations where you'd throw one of these unchecked exceptions would using the assert construct be more appropriate?
-
Item 42: Favor the use of standard exceptions
2004-08-12 22:37:06 marc_
Can someone who has the book please list the 6 exception on page 177?
-
Item 42: Favor the use of standard exceptions
2004-08-12 21:06:55 parmenion0
Not having access to page 177, I assume that you're talking about stuff like IllegalArgumentException?
I don't think this is a good idea. I want to distinguish exceptions that come flying out of the the JRE layer and those that come flying out of the various layers of my code. Throwing exceptions that polymorphically look like IllegalArgumentExceptions (for eg) blurs this boundary. Where possible I only ever tie my exceptions into the JRE hierarchy at Exception or RuntimeException.
Everyone agrees that it's not ok to throw SQLExceptions in your UI code, why should it be ok to throw JRE exceptions in your application code.
-
Item 42: Favor the use of standard exceptions
2004-08-13 11:50:42 mgrev
Maybe because SQL have very little to do with a GUI (should be shielded) but applications har very much to do with the JRE.
The stack trace tells the origin anyway.
-
Item 42: Favor the use of standard exceptions
2004-08-15 20:59:26 parmenion0
So when I catch an IllegalArgumentException I should be interogating the stack trace to decide if it comes from a java.lang.* class or one of my own? No thanks.
-
Item 42: Favor the use of standard exceptions
2004-08-12 09:23:27 hlovatt
I often find it useful to extend a standard exception to give the exception a better (more descriptive) name and to also provide conveniance constructors. But I do find that using the standard exception as the base is useful, i.e. I treat the standard exception as a classification for the type of exception. So I don't quite agree with Item 42, what is your experiance?
-
Item 41: Avoid unnecessary use of checked exceptions
2004-08-12 12:56:33 ronhitchens
This item goes hand-in-hand with Item 40: use the appropriate exception type for the situation. Do you ever create checked exceptions that only occur in unrecoverable states? What do you think of splitting an action into test-if-action-is-doable and perform-the-action methods?
-
Item 41: Avoid unnecessary use of checked exceptions
2004-08-12 16:43:59 hlovatt
Slightly off topic, I find the heirarchy of exceptions badly named and also difficult to catch just what you want to catch. In particular I would like to replace:
RuntimeException extends Exception
With:
CheckedException extends Exception
and
UncheckedException extends Exception
Where:
Exception acts purely as a base class, i.e. it does not mean a checked exception
CheckedException replaces the current Exception class
UncheckedException replaces the current RuntimeException class
Some thought would be needed as to how you could do this in a backwardly compatible mannor!
-
Item 41: Avoid unnecessary use of checked exceptions
2004-08-13 06:35:02 jimothy
I agree. I've always found "RuntimeException" to be an absurd name (I've yet to meet an exception that doesn't occur at runtime). I'm also in favor of making, at a minimum, Throwable and Exception abstract classes so they can't be directly instantiated. Error, Checked- and UncheckedException exception should probably be abstract as well.
Be specific!
-
Item 41: Avoid unnecessary use of checked exceptions
2004-08-15 10:32:22 hlovatt
I agree all the base classes for exceptions should be abstract, there is no good reason for throwing them directly.
-
Item 41: Avoid unnecessary use of checked exceptions
2004-08-12 09:29:35 hlovatt
I find this sound advice. Unfortunately some of the older API's don't follow this, presumably they were written when people were less experianced with checked exceptions than they are now. For example IOException is often thrown for a condition that I can do little about, like an operating system problem. Since 1.4 you can wrap these checked exceptions in an unchecked exception, so the problem isn't as bad as it used to be.
-
Item 41: Avoid unnecessary use of checked exceptions
2004-08-13 16:02:54 erickson
Rather than ranting at length about why this "solution" of wrapping checked exceptions in runtime exceptions is a such a big mistake, I'll simply refer you to Yishai's post on Item 40.
-
Item 41: Avoid unnecessary use of checked exceptions
2004-08-15 17:59:28 hlovatt
Yishai presents a well articulated and self consistent argument. In the extreme, following Yishai 's argument, all exceptions should be checked; because you cannot guarantee any aspect of the hardware, e.g. RAM, is operating properly.
However I find the all checked exceptions approach too pedantic: if there is a hardware problem, that I cannot deal with, then I am happy to use a checked exception to report the problem and also terminate the program. EG if I am writting a compiler and it cannot write the output file because of a hardware problem I am happy for the program to terminate and report the problem.
There may be cases were this isn't acceptable; for example in a word processor you may wish to give the user the option of trying a different location to save the file in, when a hardware problem arrises. So I go with Josh Bloch's advice of only using checked exceptions when you can reasonably expect the programmer to deal with the exception.
Also I don't interpret an unchecked exception as an un-documented exception. I see nothing wrong with, and always do, document an unchecked exception so that the user of the API knows what to expect (I include the unchecked exception in the throws clause as well as the Javadoc). It is just that if I think the user of the API probably can't do anything about the exception other than terminate the program with an error report then I find the un-checked exception useful.
-
Item 39: Use Exceptions only for exceptional conditions
2004-08-12 12:51:55 ronhitchens
This seems obvious, but it's a principle that's often violated. Have you ever written "clever" code that used exceptions like the example loops in this item? What about cases like java.util.Stack which throws an exception when popping from an empty stack? Is there a gray area here?
-
Item 39: Use Exceptions only for exceptional conditions
2004-08-24 10:05:19 johnm
But that basically just devolves the crux of the matter right back into defining what "exceptional" means.
The fact is that the exception mechanism is a seperate, "out of band" control and information channel. In the example of the Stack class, if it didn't throw an exception, how would you distinguish between the stack being empty from the stack element being null?
-
Item 38: Adhere to generally accepted naming conventions
2004-08-12 12:50:27 ronhitchens
The standard Java naming conventions should be second nature to
every experienced Java programmer. Are these conventions optimal? Is
it more important to pick one and stick with it than it is to pick the
best one? Do you adhere to the less well-known conventions, like
asType(), toType(), typeValue(), etc? Do you ever
deviate from the standard naming conventions? If so, why? Should
naming conventions evolve over time (whatever happened to prefixing
interfaces with "I")?
-
Item 38: Adhere to generally accepted naming conventions
2004-08-12 09:08:34 ashleyherring
The Sun push for standard naming conventions will always smacks of hypocriacy until they practice what they preach. Take a look at the variety of naming conventions in use within the Java libraries and you'll see what I mean....
List.iterator();
// why not List.getIterator()??;
List.size();
// why not List.getSize()??
The Java Language Specification is a standard that any compilers can use to generate valid byte-code from Java source code. I don't particularly see the Sun specified naming conventions going hand in glove with this. Why Sun's coding standard and not an industry agreed standard?
-
Item 38: Adhere to generally accepted naming conventions
2004-08-24 12:35:00 crosseye
You are right that Sun has a number of annoying inconsistencies, due at least in part to methods named before they introduced the Java Beans conventions. And while I agree with the List.size() example, I don't agree with this one:
List.iterator();
//why not List.getSize()??
An Iterator is not a property of a List. In fact a case could be made that the collection is in fact a property of the Iterator. If you call the method twice, you will get two distinct objects, that most likely won't even compare as equal(). I think that iterator() is a reasonable shorthand for the cumbersome createAndReturnANewIterator().
-- Scott Sauyet
-
Item 38: Adhere to generally accepted naming conventions
2004-08-15 20:09:22 sjasja
Should every non-void method be prefixed with "get"? Wouldn't that be pretty redundant?
-
Item 38: Adhere to generally accepted naming conventions
2004-08-14 19:18:33 farkhee
The accessor methods convention is strictly followed for properties. iterator() and size() are not properties of List class but methods that return new istance of Iterator class and calculated size of the list respectively.
-
Item 38: Adhere to generally accepted naming conventions
2004-08-12 23:40:31 jimothy
I've found the lack of "get" particularly annoying when working with, for instance, the Struts tag-libs, which rely on the Bean-naming conventions. Instead, you have to use a scriptlet to display the size of a list in a JSP (or add an extra method to your ActionForm to return the size, which might not be so bad).
There are some places where Sun needs to do a better job of eating their own dog food.
-
Item 40: Use checked exceptions for recoverable conditions and run-time exceptions for programming errors
2004-08-12 05:55:22 ronhitchens
As Bloch says, "There is some confusion among programmers as to when each kind of throwable is appropriate". Do you throw the appropriate exception type for a given situation? When you define a new checked exception, do you include information (and accessor methods) the client needs to recover? If your exception is wrapping another exception do you provide a constructor that accepts the root cause exception? Do you ever extend from RuntimeException to avoid try/catch blocks in the client? Is that ever a good idea?
-
Item 40: Use checked exceptions for recoverable conditions and run-time exceptions for programming errors
2004-08-24 10:16:46 johnm
To Type or not to Type
That is the question. --JDM.
That's fundamentally the entire war of checked vs. unchecked exceptions (and all of the rest of the wackiness around the use of exceptions). In this item, the guideline to making the decision is whether or not the system can (or even should) recover from the problem or not.
-
Item 40: Use checked exceptions for recoverable conditions and run-time exceptions for programming errors
2004-08-13 07:03:16 yishai
A potentially cogent argument for checked exceptions:
An overly simplistic way of thinking about checked exceptions is that if given the exact same input parameters, the method could sometimes throw an exception, and sometimes not, then that exception should be a checked exception. It tells the user of the API that this is an error condition that they cannot account for ahead of time, prior to calling the method. The programmer writing good code that works needs to realize that his or her application must handle that situation. If by handle that means "crash" then converting to a runtime exception may indeed be appropriate, but that is the clients choice, not the API's.
This is overly simplistic because not all appropriate checked exceptions would fit in this parameter, and there are always grey areas, but this guideline may help develop a reasonable API.
Good examples of such exceptions would be SQLException and ClassNotFoundException. The class being loaded by reflection by definition may not actually be in the classpath at runtime (if you could be sure that it would, you wouldn't need reflection). The database may not be working, or not reachable, at run time.
For operations which always fail given the same input parameters, a runtime exception could well be appropriate (such as ArrayIndexOutOfBounds) but should be documented in the javadocs so that users of the API know what parameters are valid.
One thing which regularly trips up developers, even experienced ones, is that they think they have to handle the "Exception" and not the problem it represents. For example, one of the arguments (expressed here) that checked exceptions are not appropriate is that the caller isn't in a position to deal with the exception.
This is really backwards thinking. When you call a method which relies on a database connection (or other volatile resource such as a file or server connection), you have to know that you may not get what you asked for. Returning null is only appropriate if you could expect to get null legitimately (a value in a column of a database, for example), and the method should be documented to return null. A checked exception means you have to think about what to do about the fact that the method cannot guarantee a consistent result. That is what a checked exception should be telling you: Hey, this may not work out, what do you want to do about that? How do you want to solve
the problem when the database went down, or is misconfigured, or whatever?
You should wrap the checked exception when you want to abstract it. For example, if you are writing a datasource API which can flexibly get from a flatfile, a JDBC datasource and/or an XML file, you would want to wrap the various exceptions so that calling programs don't care which one was actually used. But in all cases they are volitile, and may not be available or properly configured at runtime, so the wrapper should be a checked exception.
What to do about exceptions through an interface which cannot throw checked exceptions (Say Comparable, or an ActionListener or the like)? The first thought should be that what the API is telling me is that my operation should be guaranteed. If pressing a button hits the database, then before this method ends, I have to inform the user of failure, and do whatever I wanted to do about that failure, so the event queue can continue on its merry way not caring about the result. Otherwise, the user
presses the button, and simply nothing happens. Is that what you want? Comparable is saying: this operation has to work out, with a deterministic result. If it doesn't, it is up to the implementor to figure it out, not the client. It is either greater than, less than, or equal to. There is no option of "I can't figure it out, sometimes." If I always
can't figure it out given this input, on the other hand, this is often a ClassCastException, an appropriate use of a runtime exception.
I think this leads to the core problem: Some programmers view checked exceptions as coding problems (how to handle them), not as design considerations (how to construct a program which handles serious potential environment errors in a robust way), and therefore don't handle them properly.
If your answer is that my system assumes that the runtime environment is pristine and never fails, and that level of robustness (or lack thereof) is acceptable to you, then indeed wrap exceptions in a runtime exception. It is a step above swallowing the exception, although probably little better than catching Exception and logging the result. But that is the client's choice, not the API's. The API is telling you something will go wrong which you could not account for programmatically. It is up to you if that bothers you.
In large applications where Exception swallowing becomes the norm, I would bet that that is a symptom of larger problems such as developers producing specific functionality, not well functioning programs, and/or a highly coupled system without clear boundaries of responsibility leaving it unclear when to pass
exceptions and when to wrap them, or simply not caring about error handling. Error handling should be as much a requirement as the core functionality. If that requirement is crash early, crash often, then wrapping in runtime exceptions, or even swallowing may be appropriate. At least it is a deliberate decision, and having API's which throw checked exceptions encourage the deliberation, helping good programmers develop good code that works.
-
Item 37: Optimize judiciously
2004-08-10 01:36:27 ronhitchens
This item applies to programming in general, but reminds us that it can be tricky to optimize Java code. Does anyone disagree with the "optimize later" idea? What are some good techniques for sniffing out and correcting performance problems in Java?
-
Item 37: Optimize judiciously
2004-08-15 09:19:13 johnm
Profile. Don't speculate. --DJB
Does anyone disagree with the "optimize later" idea?
At what level are you talking about? The syntax and idiom levels? The design level? Or even the architectural level?
Another key is how do you know what the real performance requirements are? What criticality is attached to the nature of the behavior? E.g., are the requirements clear that there's a hard deadline? For example, I don't think that I want someone applying the "optimize later" guideline when working on the robotic assembly-line systems. Things get more gray (and interesting in a different way) when dealing with, say, financial systems (where people's lives may well be just as seriously affected but in less directly obvious ways).
-
Item 37: Optimize judiciously
2004-08-10 02:56:31 jimothy
There are certain cases where I do disagree with the "optimize later" idea. One notable example involves String concatenation (mentioned in an earlier thread). On several occassions, I've pointed to colleagues cases where they use String concatenation when they shouldn't. Often the response is, "Oh, I know; I'm going to come back to that and optimize it later."
Two problems: First, we often forget to come back to these things. Second, writing something twice (first the wrong way, then the right way) is wasteful of development time.
As programmers gain experience, we learn things that are almost always ineffecient, and learn to avoid writing things in those ways to begin with. There's less of a need to return to and optimize code that was written effeciently from the get-go.
A second problem is that people often take the advice to "avoid premature optimization" to the extreme, and treat it as a tail-end activity. When optimization is saved for late in the project, the code that was carefully tested is not the code that is about to be deployed.
-
Idioms
2004-08-24 10:23:27 johnm
The case of things like string concatenation for debug statements is an interesting segue into the notion of idioms. The creation and use of idioms are where knowledge can be pushed down into the fingers (as it were) so that the benefits need not take up much time and basically no cognitive energy (i.e., it doesn't have to count as one of the decisions that needs to be considered and made).
-
Item 37: Optimize judiciously
2004-08-11 21:43:50 archangel
If someone points out an obvious inefficiency in my code I say "Oh, I know; I'm going to come back to that and optimize it later if I need to." As you can guess I'm a fan of "Item 37: Optimize judiciously ".
I think there is a difference between optimization and writing software using good practices.
* It is good practice to use StringBuffer when performing a lot of String manipulation.
* It is good practice to explicitly dispose of external resources (such as database resources) rather than let them be closed when garbage collected.
* It is good practice to factor out shared resources from loops:
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
for(Date d : dateList) {
System.out.println(formatter.format(d));
}
rather than
for(Date d : dateList) {
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
System.out.println(formatter.format(d));
}
Good practice will save you from incurring unnecessary inefficiencies. If you don't follow good practices and your program is too slow you will have to optimise these areas.
I don't think "Item 37: Optimize judiciously" is saying don't follow good practices; don't code with no consideration to efficiency. It's a warning about being too pre-occupied about speed. Rather than spending your time introducing object caching etc. you could be implementing new funcitonal features.
Manager: "Is your project finished yet?"
Developer: "No, we haven't started some features yet....but the ones we've done are really quick"
Manager: "You're fired."
> "When optimization is saved for late in the project, the code that was carefully tested is not the code that is about to be deployed."
Really? You don't keep your unit tests?
-
Item 37: Optimize judiciously
2004-08-12 02:40:12 jimothy
"Really? You don't keep your unit tests?"
I do, but unit tests are only one part of testing. And in an ideal world, unit tests are, well, ideal. Reality is something different.
-
Item 34: Refer to objects by their interfaces
2004-08-10 01:32:58 ronhitchens
This is a key concept that's often not well understood. Do you program to interfaces as much as you should? Are interfaces used too much or not enough? Is it appropriate to create interfaces even when you know there will be only one implementation class?
-
Item 34: Refer to objects by their interfaces
2004-08-10 23:05:06 jwenting
define "know".
I've had cases where I had a single class (I thought) but then an extra requirement creeps in causing for a slightly different execution flow which is best handled by just taking a slightly different implementation of that same class.
Voila, you now have 2 classes already.
Another option here would be abstract base classes, which serve a similar purpose.
-
Item 34: Refer to objects by their interfaces
2004-08-11 00:45:41 jimothy
So, is it better to refactor when the need for the second class arises, or code in anticipation of the second class and begin with an interface or abstract class?
Obviously, it depends on the particulars, but in general, I go with refactoring on the you-ain't-gonna-need-it principle.
-
Item 34: Refer to objects by their interfaces
2004-08-15 09:07:14 johnm
Do you use condoms?
As with jwenting's question/comment about what definition of "know" is being used, the crux from a type issue what is the point of the class? I.e., is it just an helper class that is completely internal to a module and is never seen by anything external or not? The point with this item is that this (type issues) are a place where the bias should be clearly to use interfaces if there's any potential doubt at all.
-
Item 34: Refer to objects by their interfaces
2004-08-16 11:25:59 jimothy
"Do you use condoms?"
Tha's a little personal, don't you think?
I don't follow your analogy. Refactoring can be done to improve previously designed code with the benefit of hindsight. Hindsight isn't of much help when it comes to issue of pregnancy or nasty viruses (avoiding obvious moral issues).
-
Item 34: Refer to objects by their interfaces
2004-08-16 05:04:41 johnm
Refactoring is a marvelous tool that we in the software business have but even refactoring is notsilver bullet.
There are some categories of risk for which the pro-active mitigation costs are much smaller than the costs of dealing with the potential negative outcomes.
In cases where the code is completely internal to a module then the ripple costs of reactive refactoring are well contained, especially in terms of local control of all of the uses. In cases where e.g., all of the uses are not locally controlled, change-risk mitigation by the use of clear type information via interfaces is a small cost to pay.
-
Item 34: Refer to objects by their interfaces
2004-08-10 10:02:02 jimothy
A classic example of this is to say List things = new ArrayList()
instead of ArrayList things = new ArrayList().
More important, methods are more flexible if they accept List , instead of ArrayList, as parameters. Better yet, accept Collection.
-
Item 36: Use native methods judiciously
2004-08-09 18:35:25 ronhitchens
Native methods can cause major heartburn if they're not rock solid. Is anybody using native methods on a regular basis? What sort of thing still requires resorting to native methods?
-
Item 35: Prefer interfaces to reflection
2004-08-09 18:34:05 ronhitchens
Reflection is powerful but awkward and error-prone to use. In 1.4, reflection performs much better, but does that mean we should use it any differently? Should it be confined to the suggested uses? Is reflection over used or under used?
-
Item 35: Prefer interfaces to reflection
2004-08-11 23:10:08 archangel
If you can do it elegantly with an interface do that. Otherwise it's a judgement call.
-
Item 33: Beware the performance of string concatenation
2004-08-06 00:11:07 ronhitchens
This has to be one of the most common Java programming mistakes. Do you understand how implicit string concatenation is performed? Do you know when it's safe to concatenate strings with the "+" operator and when you should use a StringBuffer object to build up a string?
-
Item 33: Beware the performance of string concatenation
2004-08-14 10:05:37 kunitz
This rule must be modified for Java 5.0. Java 5.0 introduces a StringBuilder class. This class will be somewhat faster than StringBuffer because it doesn't synchronize. So everywhere where you would have been used StringBuffer locally in a single thread, you should use StringBuilder with Java 5.0.
-
Item 33: Beware the performance of string concatenation
2004-08-17 11:19:23 tackline
Try a microbenchmark comparing StringBuilder to StringBuffer. You should find almost identical performance. Repeatedly synchronising from the same thread isn't really that expensive on vaguely modern JVMs. In 5.0 the Java Memory Model (JMM) changes so that thread local synchronisations can be discarded.
OTOH, as ever it may take some time for running code to be optimised. You may find improvements by changing large amounts of frequently used code over from StringBuffer to StringBuilder. I guess maintaining pre-5.0 StringBuffer code alongside shiny new StringBuilders can confuse.
-
Item 33: Beware the performance of string concatenation
2004-08-09 19:28:46 smesh
If this supposed to be a tip and/or guide please provide an explanation...
-
Item 33: Beware the performance of string concatenation
2004-08-10 01:30:58 ronhitchens
Strings are immutable, so concatenating strings with "+" is really an implicit operation that makes a StringBuffer, appends each of the operands, then constructs a new String object as the result. This is fine for one-offs, but loops that add to a string on each iteration are actually causing a lot of StringBuffrer and String objects to be created needlessly. In such a case you want to create single StringBuffer instance and append to it as many times as needed.
-
Item 32: Avoid strings where other types are more appropriate
2004-08-06 00:10:34 ronhitchens
Do you ever work with the string representation of a value rather than the natural type? How about strings to represent aggregate values? What are the problems with this?
-
Item 31: Avoid float and double if exact answers are required
2004-08-06 00:09:54 ronhitchens
A floating point value is an approximation. How many bugs arise from ignorance of that fact? Have you ever tried using floats or doubles to represent money? How did it work?
-
Item 31: Avoid float and double if exact answers are required
2004-08-10 00:27:52 vikstar
The c++ package called LEDA by Algorithmic Solutions http://www.algorithmic-solutions.com/enleda.htm can use rational numbers. Something like this would not be to hard to implement in Java. You can then exactly store any number that is not irrational if you use BigInteger to represent the numerator and denominator.
-
Item 31: Avoid float and double if exact answers are required
2004-08-09 10:51:56 d_bleyl
This a major problem. Division, persistence and items with fractions of a cent really complicate things.
I've tried to address this issue by writing tons of unit tests. But what is the correct way to handle such values? If you were teaching someone java, and their first application contained monetary values, how would you advise them to code it? Reach for a library? BigDecimal? I hear the problem frequently, but not the solution.
-
Item 31: Avoid float and double if exact answers are required
2004-08-09 07:14:09 c_jinx
I have been tought to create a class Money. This class would represent the currency and the amount. For the currency I'd use the class java.util.Currency (I've been lucky to work on jdk 1.4), and for the amount I'd use BigDecimal for all operations.
-
Monetary values are not really "exact", will primitive long do?
2004-08-16 03:37:17 denka
Isn't BigDecimal too slow for bulk operations? I've heard of approach to use long primitives to represent money values, rounded to some fraction of a cent, like here: $1.01 ~ 101000L, is there a reason this approach will not always work? (It surely should work in many cases.) In which cases will it not? Hope this is not OT ;)
-
Item 31: Avoid float and double if exact answers are required
2004-08-09 22:58:56 d_bleyl
This is a popular technique. I've seen many Money classes on the net, but I'd really like to know why
there isn't one in the jsdk yet.
What rounding strategies do you use with your BigDecimal?
-
Item 31: Avoid float and double if exact answers are required
2004-08-12 06:13:05 archangel
Whichever is appropriate. Our company writes payroll software and different rounding strategies are needed in different places.
-
Item 31: Avoid float and double if exact answers are required
2004-08-15 19:24:22 d_bleyl
This is the same technique that I use. Frequently,
I use the Strategy pattern to make rounding configurable and more explicit. I've also found that using this pattern allows for very interesting composite rounding strategies. Works nicely for systems used to calculate pricing where the rounding strategy might previously have been expressed as one very long round() method with lots of switches.
-
Item 31: Avoid float and double if exact answers are required
2004-08-09 08:58:04 uhf
I worked for an online travel company that went bust three years ago. They used doubles for currency even though one astute developer pointed out this was wrong. Sure enough in time the total price of a flight came up with an extra penny that appeared from nowhere.
I'm surprised there isn't a Currency class included in Java from the start as it seems like a fundemental requirement and would have prevented many a project going up the wrong avenue.
-
Item 31: Avoid float and double if exact answers are required
2004-08-09 09:00:07 uhf
I mean one that includes the concept of amounts!!
-
Item 31: Avoid float and double if exact answers are required
2004-08-06 03:09:43 stepanrutz
Interesting subject. I have seen monetary spreadsheet logic based on floating point values. But hey, the relativ error stays small. But Joshua Block is right (as always) when he makes this point. It cannot be repeated often enough that monerary data and floating point values don't go together well. For example Java the following code does nothing: float f = 1E8f; f += 1; Regards, Stepan
-
Item 29: Minimize the scope of local variables
2004-08-06 00:07:45 ronhitchens
Properly managing the scope of local variables is a skill that's often under-appreciated. Do you always try to use the tightest possible scope for locals? If you're a former C coder, do you still have the habit of declaring all the locals at the beginning of the method? Do you always declare loop variables inside for loops? Even iterator variables? Do most of your local variable declarations include initializers?
-
Item 29: Minimize the scope of local variables
2004-08-13 06:24:06 rabreuc
I believe in using local variables just when you need them. Its nonsense declaring a variable at the top lines and using them 3000 lines after. Also a resources cost, even if its not that "much".
-
Item 30: Know and use the libraries
2004-08-05 17:08:47 ronhitchens
Have the libraries become too large to know? Is there a core subset that every experienced Java programmer should know intimately? Does the 80/20 rule apply (80% of the problems can be solved by 20% of the libraries)? Do you know and use the right 20%?
-
Item 30: Know and use the libraries
2004-08-15 01:51:09 johnm
Have the libraries become too large to know?
(A) The Java "libraries" are to libraries what Ada is to languages. :-? :-)
(B) It's not just the sheer size of the Java libraries that have become problematic but the fact that so many of them are so complicated in their real use.
-
Item 28: Write doc comments for all exported API elements
2004-08-01 18:44:28 ronhitchens
Everyone can surely agree that APIs designed for others to use
should be documented. But are Javadoc comments that are out of sync
with the code helpful or harmful? Does every public method need a
Javadoc comment? Does "void remove(Element element)" need a lot of
explanation? Are comments deodorant for smelly code? Shouldn't the
contract between a method and its client be expressed in the syntax of
the language so it can be enforced?
This wraps up Chapter 6, Methods.
-
Item 28: Write doc comments for all exported API elements
2004-08-04 00:45:28 landrus
In General, you are right. With a few exceptions. When u have a interface, that can be implemented by the user (see Swing for the datamodels that are used by the ui, e.g. TreeModel, TableModel...), you define the contract, that the user has to fullfill mostly by the documentation. Often the name and the parameters of the interface won't be sufficient to let the user know, what this method has to do. If the documentation is wrong or lacks information, you will certainly have erroneous behaviour of your code on the api or client side.
You can carry this to the extremes, when you think of specifications. There you only have documenation and no source code at all. So everything depends on the way you express things in the documentation.
Greetings,
Christian.
-
Item 24: Make defensive copies when needed
2004-07-28 19:28:36 ronhitchens
Is this good advice that's not heeded often enough? When you store an object reference do you assume the object won't change? What if it does? Do you ever return internal object references to an untrusted client? Were you aware of the requirement to copy parameters before testing them, and that cloning parameters is not safe? Are defensive copies wasted effort in a trusted environment?
-
Item 24: Make defensive copies when needed
2004-08-05 05:02:39 johnm
In code reviews and assessments, I find a lot of subtle and not-so-subtle problems that could be solved by making defensive copies.
IMHO, a fundamental problem is that each developer seems to have a different notion of the true scope and extent of the information in a system. All too often, that manifests through different assumptions and presumptions about how to get at and modify the information.
Whether or not two information manipulators are or are not inside the same trust boundary is just one (simple and obvious) dimension. I like to look at the (boundaries) of the various mental models of the system. IOW, at what point does a (lack of making a) copy significantly (increase /) decrease the cognitive complexity.
-
Item 23: Check parameters for validity
2004-07-28 19:26:49 ronhitchens
Common sense. But how often have you seen code that doesn't sanity check its parameters? Do you validate the parameters of all the public methods you write? Do you use assertions in non-public methods? Do you add or remove validation when you change the accessibility of a method? Do you assure that all class invariants are satisfied by constructors? What about Design By Contract?
-
Item 23: Check parameters for validity
2004-08-02 21:13:40 matthewmorris
If it's the outer face of your software then, yes, check parameters aggressively for validity.
You can use assertions in non-public methods, but they shouldn't be for the same kind of thing as you're checking for in the public layers, eg non-null checking.
Yes, you need to add/remove validation when methods move into/out of the public layer: if there isn't a well-defined layer in this respect, the whole interface loses focus. I have seen many projects undergo a complete rewrite because of a failure to maintain well-defined service layers.
-
Item 23: Check parameters for validity
2004-08-15 01:44:06 johnm
I have seen many projects undergo a complete rewrite because of a failure to maintain well-defined service layers.
For each case of that, how many cases have you seen where the developers more or less consciously destroy layering e.g., in the name of "performance"? Were those cases of just ignoring the architecture and/or design or did they mutilate existing code too?
-
Item 23: Check parameters for validity
2004-07-28 22:00:19 marc_
Quite often a public method will throw an exception 'natuarally' if the wrong parameters are passed in, so I'd be inclined no to check in these cases - even where it would only be a simple 'if' statement.
But I agree that parameters should be checked where the error would not be immediately apparent - the classic case is checking for null parameters in the constructor.
-
Item 19: Replace structures with classes
2004-07-27 11:25:25 ronhitchens
Items 19 through 22 (Chapter 5) concern substitutes for C
constructs.
Is a Java class that contains nothing but public fields equivalent
to a C struct? Is a C struct-like class something that has a place in
Java? Do you use struct-like classes in package-private or private
nested contexts? How is a bean object, with public setter and getter
methods for every private field, effectively any different?
-
Item 18: Favor static member classes over nonstatic
2004-07-26 11:36:03 ronhitchens
Nested classes are a common source of problems. Do you feel you fully understand the four types of nested classes (static member, nonstatic member, anonymous and local) and their proper usage? Is the overhead of the implicit reference to the enclosing object a deciding factor when choosing static vs nonstatic? What are the tradeoffs in terms of functionality and/or visibility? Are anonymous classes a good idea? Do they clutter the code and make it harder to read? Have you ever used a local class?
-
Item 18: Favor static member classes over nonstatic
2004-07-26 17:29:06 archangel
I would say that it is the minority of Java developers that understand inner classes. I certainly had very little understanding until I had to learn them for the Programmer's exam.
Nested classes have a few uses:
- As a suppliment to Java's (IMHO inadequate) visibility modifiers.
- Creating mock objects used in unit tests
-
Item 18: Favor static member classes over nonstatic
2004-07-27 14:54:09 adamjaph
I vastly prefer true mock objects from any of the several frameworks (DynaMock is my current favorite) to home grown mocks made from static inner classes. Occasionally I might use an anonymous inner class as a stub if I just need a placeholder and don't need to actually verify any behavior. However, when I find myself doing this it usually means that there is a simpler solution than the one I am currently implementing.
-
Item 18: Favor static member classes over nonstatic
2004-07-27 03:55:18 johnm
They also work well for creating "white-box" tests and test drivers.
Note thought that there seems to be a "light" religious war over the co-location of tests with the code under test and whether or not it's ever good to do this sort of white-box testing as "unit testing."
-
Item 17: Use interfaces only to define types
2004-07-26 11:34:19 ronhitchens
Defining constants in interfaces was encouraged at one time and many Java programmers still think it's a good idea. Have you ever created an interface that only defines constants? Do you agree that defining constants is not a good use of interfaces? If it's a bad idea, why is it allowed? Should it be deprecated? Do 1.5-style static imports eliminate this problem?
-
Item 17: Use interfaces only to define types
2004-07-27 07:56:58 c_jinx
1.5-style static imports do not eliminate the problem and to be honest I don't like the new idiom. It obscures the origin of the constant. (Imagine you're reading code someone else wrote, you come across a constant 1.5 style, and you're left wondering what it is, what value it is holding and where it has been defined).
Constants defined in interfaces should have been treated as compilation errors by javac. That would have eliminated the problem.
-
Item 17: Use interfaces only to define types
2004-07-26 17:22:34 archangel
We do this in my company. However, I seem to remember that in Effective Java he's talking about defining constants in interfaces and "importing" them by making your class "implement" the interface (I'm at work ATM).
This *is* bad because it doesn't make any sense from a hierarchical point of view.
You can just import the interface and then qualify the constant with the interface name e.g. USER_MESSAGE.WELCOME. I think 1.5 static imports will help in that you won't have to qualify the constants.
-
Myth
2004-07-27 03:33:23 atrajano
You are not forced to implement the constants in your methods. Its not a good idea anyway, but it is possible.
I wrote up on this in my blog. There have been no technical reasons to avoid it and it makes your code more consise if you need it to be.
public interface Foo {
// NOTE NO PUBLIC STATIC FINAL
String BAR = "abc";
}
compared to
public class Foo {
public static final String BAR = "abc";
}
-
Myth
2004-07-27 08:07:08 zodiac
I wasn't sure what your point was here so I checked the blog entry and realised that all you're looking for is to save the effort of typing 'public static final' at the start of the line? OK, 'public' is redundant but 'public static final' is such an ideomatic declaration that it instantly communicates the intent of the declaration: a *constant*. You have to be paying pretty close attention to the code to realise what's going on with your ideom.
And that's really what these debates come down to: ideoms based on the implied meaning of stretching the defacto semantics of the language without warping them.
Interfaces are intended to define APIs or, at a semantically higher level, capabilities or natures of the classes that implement them, right? It just so happens that, as a convenience, you can define constant members in an interface (no doubt intended to substitute for the missing enumerated types added by Tiger).
Defining an interface soley in order to take advantage of this convenience warps the semantics of the language; implementing that interface just to avoid qualifying the name of the constants when you reference them totally twists those semantics.
All of which is why I couldn't believe enumerated types were not a supported language feature when Java first came out and why I was so happy to see them in 1.5.
You should hear my rants on generics! :-)
L.
-
The right tool for the job?
2004-07-27 04:47:40 ronhitchens
Bloch argues that interfaces should only be used to define types. Isn't defining an interface that's never implemented missing the point of interfaces?
-
The right tool for the job?
2004-07-27 05:19:43 adamjaph
Your title says it all. Until recently Java developers haven't had the right tools to handle constants and enumerations so various idioms have emerged that really abuse the language constructs (The constant interface is certainly one of these.) Now with the ability to import constants in 1.5 and the new Enum type, hopefully this can change (e.g don't use constant ints to define an enum, use an Enum; and don't use constant interfaces to collect and import constants, put the constants in the class that they belong to logically and import them where they are used.)
-
Item 16: Prefer interfaces to abstract classes
2004-07-25 15:44:45 ronhitchens
There seems to be a perception that interfaces and abstract classes are almost the same thing. How did this perception come about? When is using an abstract class appropriate? Do you ever pair interfaces with abstract skeletal implementations? Have you ever used this technique to simulate multiple inheritance? Have you ever had to evolve an interface or abstract class? Was in made easier or harder by your choice of interface or abstract class?
-
Item 16: Prefer interfaces to abstract classes
2004-07-28 21:39:15 marc_
Two points:
1. Don't introduce this design when a single class would do!! I once worked at a place where many of the objects were defined like this: "PersonInterface", "AbstractPerson", "PersonImpl". It added a heap of complexity, and for what? Maybe later you will want to create another implementation of 'Person', but *that* is the time to introduce this design, not at the beginning.
2. If you're not writing a public API and have access to all the places where the abstract class is used, does it really matter that much? Is it that hard to introduce an interface later?
-
Item 16: Prefer interfaces to abstract classes
2004-07-27 10:31:04 tackline
From the perspective of client code there is no significant difference between interfaces and abstract classes. So for most applications, where subtypes are strictly limited, you might as well stick with an abstract class defining both interface and your abstract skeletal implementation. Having a separate interface just makes for more code with JavaDocs split from common implementations, which makes maintenance much more of a problem with no upside.
-
Item 16: Prefer interfaces to abstract classes
2004-07-27 04:11:12 johnm
This is a case where there is no good, complete answer (in Java). Basically, Java's approach to the mingling/separation of typing vs. implementation is a bit of an odd, incomplete, and in-between thing.
Fundamentally, there's a lack of grip w.r.t. the (management of) meta-data of a type and an implementation. A clear example being the lack of real version information. I.e., critical support for dealing with various aspects of time (i.e., evolution) in the type models in Java are just plain missing. [It's interesting to note how this same "myopia" (if you will) plays out in a lot of other areas and at various levels in Java (and Sun :-).]
-
Item 16: Prefer interfaces to abstract classes
2004-07-29 18:07:27 davidbullock
JDK 1.5 annotations are a platform for fixing the versioning problem.
-
Item 16: Prefer interfaces to abstract classes
2004-07-27 04:56:52 ronhitchens
Do you think Java's deficiencies in this area are a fatal flaw? Or is every langauge an imperfect compromise that reflects its creators' priorities and history? Is 1.5 an evolutionary step in the right direction?
-
Meta-data management
2004-07-27 06:36:47 johnm
Do you think Java's deficiencies in this area are a fatal flaw?
Hm... At what level? Obviously, looking at this in terms of Java's popularity in a historical perspective would seem to say "no". Looking at this from the point of view of someone who cares about writing great software, it's certainly at least a serious flaw.
Or is every langauge an imperfect compromise that reflects its creators' priorities and history?
Yes and no. It's certainly true that every language is a compromise across a complex feature space. However, there's a lot of room for improvement in dealing with crucial management issues such as this.
Is 1.5 an evolutionary step in the right direction?
Nope. What new features in Tiger do you think (help to) address this fundamental issue?
-
Item 16: Prefer interfaces to abstract classes
2004-07-26 17:13:26 archangel
I totally agree with this. I think it is the only way to code. No fragile base class problems!
-
Item 16: Prefer interfaces to abstract classes
2004-07-25 20:32:13 parmenion0
No! Have you ever tried to suport backwards compatibility while adding new functionality to a public API? If other people are implementing your interface, you can't add methods to it (without mixin rubbish). If they're implementing your abstract class, no problem.
Use an interface if you've only got a couple of methods and you're 100% sure you're never going to need more. Otherwise use an abstract class.
-
Item 16: Prefer interfaces to abstract classes
2004-07-26 04:30:18 zodiac
That's a bit of a falacy; adding methods to an abstract class isn't as bad for backwards compatibility as adding them to an interface, but there's certainly an impact.
There's an argument for using an abstract class if there is significant functionality you can provide but there's also significant functionality that needs to be supplied; that's the most common justification. But you should really consider if you can break those two 'aspects' of the class into a concrete class and an interface.
IMHO, the best reason for using an abstract class is if you have an API you *expect* to evolve, in which case you can tell clients of the API not to add additional methods in their implementation (or expect that they may have to rename them later).
Even adding new methods to non-final public classes can break backwards compatibility, after all.
L.
-
Item 16: Prefer interfaces to abstract classes
2004-07-26 17:34:43 archangel
True. Sounds good.
-
Item 16: Prefer interfaces to abstract classes
2004-07-26 04:21:56 bengriffiths
In my experience, this is exactly the situation when programming to interfaces pays off. I've faced exactly the problems you're describing and several years ago would have rushed to your defence on this point; however, the more code I write, and the more I have to maintain, the more I consider abstract classes to be dangerous, incomplete objects that survive change badly.
A particular problem: it's reasonable to assume that if you add methods to an abstract class, previous versions' overriding classes will not know about these methods and therefore may have faulty implementations. Those that do know about the methods can implement a more specialized interface.
My advice is to always seek to replace Abstract classes with an object that has a specific defined behaviour that may take an interface as a parameter
A trivial example that should be familiar from the java libraries, if you have an abstract class com.example.CollectionImpl with an abstract sort() method, you'll create a class that is somewhat extensible but brittle, but if you instead create an interface com.example.Sorter that has a sort(Collection c) method that conforms to an com.example.Collection interface you'll find it survives well, and can be used/removed/ignored safely.
-
Item 15: Design and document for inheritance or else prohibit it
2004-07-25 15:42:49 ronhitchens
This item makes it clear that there are many things to consider when designing a class to be extended. Do you think about the consequences of self-use of overridable methods? Do you document such cases? What about constructors calling overridable methods? When designing for inheritance do you think about the commitment you're making by exposing methods and fields? When writing classes that you don't intend to be extended, do you take steps to prevent inheritance? Is it better to prevent inheritance by default, or allow it?
-
Item 15: Design and document for inheritance or else prohibit it
2004-07-27 05:11:47 adamjaph
OO programmers spend a lot of time talking about reuse. If reuse is really an objective then allowing inheritance is a good idea since you never know how the code you write today could be useful in the future. On the other hand, if you are writing framework code or any sort of API that will be used by other programmers then you need to consider security and uses of your code that are unintended and, perhaps, harmful. In these cases, and these cases only, preventing inheritance is a good idea. Having said that, I can recall several times when I have heard of or experienced difficulty working with someone elses code where they made constructors final or otherwise prevented inheritance uneccessarily and it really got in the way of any ability to reuse their code. If there is any possibility that code will be reused then it is imperative that you avoid creating uneccessary obstacles.
-
Item 15: Design and document for inheritance or else prohibit it
2004-07-27 05:24:36 ronhitchens
If a class can't be extended, you can always make use of it via composition and forwarding methods to simulate inheritance, correct? If a class prevents inheritance, is there something the author of that class is protecting against. If a class allows inheritance, has the author protected against the potential problems of doing so?
-
Item 15: Design and document for inheritance or else prohibit it
2004-07-27 06:25:41 johnm
Indeed!
However, note that Java's support for composition is seriously lacking relative to its support for inheritance. E.g., a programmer has to basically manually construct the compositional forwarding. [Yes, I know about various introspection based libraries/tools -- they help prove my point.]
-
Item 15: Design and document for inheritance or else prohibit it
2004-07-28 12:58:27 dog
I wonder if a JSR could be written to enhance Java with some sort of easy composition syntax.
-
Kitchen-sink-ism?
2004-07-27 04:18:06 johnm
Is it better to prevent inheritance by default, or allow it?
Given the fact that most code written in Java is "usage" rather than "model" and how the various needs that must be addressed to make something usefully/safely/etc. inheritable, it would seem that the answer is to that question is pretty clear.
However, would that added complexity really be worth it for an in-between language like Java? Isn't part of the problem with computer languages, in general, is that we, as an industry, have this insidious habit to try to make all of the languages into a swiss army knife?
-
Item 14: Favor composition over inheritance
2004-07-25 15:40:57 ronhitchens
Today begins the second week of our discussion of Effective
Java. This week we lead of with three items that speak to the very
nature of Object Oriented programming.
Inheritance is one of the bedrock concepts that underlie OO
languages. It seems ironic then that inheritance is so often the wrong
thing to do. Is recognition of the pitfalls of inheritance a sign of
maturity in an OO programmer? How often have you used inheritance when
composition would have been more appropriate? Did you do it after you
knew better? The problem of fragile base classes is well understood,
but is it understood widely enough? Are IDE tools that auto-generate
forwarding methods for contained objects a good thing?
-
Item 14: Favor composition over inheritance
2004-07-27 05:00:16 adamjaph
I think that the problem is that many developers treat inheritance as a shortcut to spread certain behavior around an application or to implement a pattern without considering the unique semantics of inheritance. In other words, what does this use of inheritance mean within the domain? Where is the is-a/type-of relationship? I'm often amazed at how Java developers find these complex uses for inheritance without considering what it means. Some of these guys need to put down their Java books and pick up some CRC cards.
-
Item 13: Favor immutability
2004-07-22 12:11:55 ronhitchens
How often do you write immutable classes? How many of your "regular" classes could be made immutable? Do you consider immutability when creating a new class? When you set out to write an immutable class, do you obey all five rules of immutability? Are you guilty of writing init methods in addition to constructors (one my personal pet peeves)?
-
Item 13: Favor immutability
2004-07-23 02:50:44 sblundy
I think immutability is great, making many things simpler. I tend to make smallish, and not so smallish, data classes immutable. Things get tricky when the classes are complex. At a certain point using a single constructor is a pain. Builders seem to help then, but youre getting into the range of why bother, why do you need such a big immutable hunk of memory floating around?
-
Single Constructor?
2004-07-27 05:12:44 ronhitchens
I don't think it's a requirement that an immutable object have only one constructor. Just that all initilization must be done in the constructor and that the object's state not change thereafter. For example, java.math.BigInteger has six constructors but instances are immutable.
-
Item 12: Minimize the accessibility of classes and members
2004-07-22 12:09:34 ronhitchens
Encapsulation is a subject that most programmers think they understand but often don't practice very well. Do you automatically choose the least accessible scope when declaring classes, methods and fields? If not, why not? Should you consider security when you're writing all the code? What about thread safety if you're not creating threads? Are there any counter-arguments to minimizing accessibility?
-
Item 12: Minimize the accessibility of classes and members
2004-07-26 01:43:26 jimothy
Probably the biggest violation of this principle (both personally an in the Java community at large) is our reliance on getters are setters. The user of getters and setters is such a common idiom, and IDEs make it so easy to automatically generate them, that is easy to fall into a habit of provides getters and setters for nearly every property in each class.
-
Item 12: Minimize the accessibility of classes and members
2004-07-28 22:19:43 marc_
Probably the biggest violation of this principle (both personally an in the Java community at large) is our reliance on getters are setters.
I don't agree. In almost the places I've seen getters/setters, it is to expose a property that should be public. Some people say that properties should not be exposed, but I've never heard a convincing argument for this.
-
Item 12: Minimize the accessibility of classes and members
2004-07-30 03:01:20 afishionado
I say "Bingo".
Seriously, the only time getter/setter methods seem to make sense is if their bodies are more than one line long (perhaps they contain logic to only conditionally allow access to internal state, or perhaps they fire off a state change event), for some reason you want a getters but not setters, or if the getter/setters are, say, synchronized for thread safety.
Otherwise, why not start making these things public? Sometimes I see Java classes that really function like C++ structs; they are containers for data manipulated by other classes, not real abstract objects. These generally seem to be situations where OOP is probably not the best solution to a problem, but Java forces you to use a class.
Certainly, there are many, many cases where providing public access to internal state makes no sense, but that's not always the case.
-
Item 12: Minimize the accessibility of classes and members
2004-08-02 00:39:31 yishai
<I>Seriously, the only time getter/setter methods seem to make sense is if their bodies are more than one line long</I>
<P><P>
Although that may be the current state of affairs, that may well change in the future, causing a large amount of maintenance if you were not using getters and setters in the first place.
Say, for example, you wanted to add a listener later.
One thing to remember about Effective Java is that it is really written with published API's in mind. Making a field public is something you can never back out of without breaking backwards compatability, so it hardly seems worth the risk.
-
Item 12: Minimize the accessibility of classes and members
2004-08-02 02:36:13 afishionado
I'll concede that.
Though, there are situations where it doesn't make sense that you would later want to extend the class in that way.
-
Item 12: Minimize the accessibility of classes and members
2004-08-02 02:53:26 afishionado
Other thoughts: You have more flexibility to modify the interface in situations where you and/or the person in the next cubicle are the only people using your class. Then again, this probably isn't a good thing to rely on, either.
|