More on this book
Community
Kindle Notes & Highlights
There are a number of obvious violations of this principle in the Java platform libraries. For example, a stack is not a vector, so Stack should not extend Vector.
Haven’t used stack so had no idea this was done. Wow. Though I admit there is definitely a period in my career when I would've considered this approach. But then again, I shouldn't have been writing the Java API then (nor even now, surely ;) ).
First, the class must document precisely the effects of overriding any method. In other words, the class must document its self-use of overridable methods.
Heh. The internal method calls in effect become part of the API: you guarantee forever which calls will be made when. I see his point, but feel this shouldn’t be needed, unless you’re trying to expose hooks that subclasses intentionally provide. But this feels more like an abstract class pattern than inheritance from concrete class.
Designing for inheritance involves more than just documenting patterns of self-use. To allow programmers to write efficient subclasses without undue pain, a class may have to provide hooks into its internal workings in the form of judiciously chosen protected methods
I like seeing this done well. It can really make development within a larger framework very efficient when repetitive, annoying tasks are just taken care of for you. I see a lot of @nnotations trying to abstract this even further from the code, but prefer explicit abstract patent classes to provide this. It’s hard to know all of the needed hooks to create though and you may often revisit the hooks design over time. If you own all the code, no problem though, this ends up being small cost when amortized over years. But woah, if you’re building a framework for others, look out! :)
The only way to test a class designed for inheritance is to write subclasses. If you omit a crucial protected member, trying to write a subclass will make the omission painfully obvious. Conversely, if several subclasses are written and none uses a protected member, you should probably make it private.
Thumbs up. But you might need to think more creatively, get more diverse ideas from others. Guaranteed someone will come up with a creative case that breaks your model....
Ah, right after this they say to create at least 3 subclasses and that at least one should be authored by someone else. Yes.
Constructors must not invoke overridable methods, directly or indirectly. If you violate this rule, program failure will result. The superclass constructor runs before the subclass constructor, so the overriding method in the subclass will get invoked before the subclass constructor has run. If the overriding method depends on any initialization performed by the subclass constructor, the method will not behave as expected.
The Cloneable and Serializable interfaces present special difficulties when designing for inheritance. It is generally not a good idea for a class designed for inheritance to implement either of these interfaces because they place a substantial burden on programmers who extend the class.
If you do decide to implement either Cloneable or Serializable in a class that is designed for inheritance, you should be aware that because the clone and readObject methods behave a lot like constructors, a similar restriction applies: neither clone nor readObject may invoke an overridable method, directly or indirectly. In the case of readObject, the overriding method will run before the subclass’s state has been deserialized.
When there is an obvious implementation of an interface method in terms of other interface methods, consider providing implementation assistance to programmers in the form of a default method. For an example of this technique, see the removeIf method on page 104. If you provide default methods, be sure to document them for inheritance using the @implSpec Javadoc tag (Item 19).
How different really is interfaces with default implementations from multiple inheritance though? No state I guess, that’s a good point.
You can, however, combine the advantages of interfaces and abstract classes by providing an abstract skeletal implementation class to go with an interface.
A minor variant on the skeletal implementation is the simple implementation, exemplified by AbstractMap.SimpleEntry. A simple implementation is like a skeletal implementation in that it implements an interface and is designed for inheritance, but it differs in that it isn’t abstract: it is the simplest possible working implementation. You can use it as it stands or subclass it as circumstances warrant.
In Java 8, the default method construct was added [JLS 9.4], with the intent of allowing the addition of methods to existing interfaces. But adding new methods to existing interfaces is fraught with risk.
This is not good motivation to introduce default implementations though, right? Perhaps better off adding new interfaces and dealing with that pain? I’ll bet some default implantation just throw not-implemented exceptions, right?
If you make heavy use of the constants exported by a utility class, you can avoid the need for qualifying the constants with the class name by making use of the static import facility:
I don’t really like this static import thing. I think it assumes everyone is using an IDE, as it hides where things are coming from. One of the things o liked about java (especially when diving into unfamiliar codebase) was how explicit everything needs to be.
// Typesafe heterogeneous container pattern - implementation public class Favorites { private Map<Class<?>, Object> favorites = new HashMap<>(); public <T> void putFavorite(Class<T> type, T instance) { favorites.put(Objects.requireNonNull(type), instance); } public <T> T getFavorite(Class<T> type) { return type.cast(favorites.get(type)); } }
Do we need to ensure that type is not null here? type.cast() seems problematic in that case?
But I’m guessing casting null to any (class) type goes fine, in the case the Set doesn’t have an entry for the given type?
Too lazy to test right now. :(
The class literal List<String>.class is a syntax error, and it’s a good thing, too. List<String> and List<Integer> share a single Class object, which is List.class. It would wreak havoc with the internals of a Favorites object if the “type literals” List<String>.class and List<Integer>.class were legal and returned the same object reference. There is no entirely satisfactory workaround for this limitation.
Naive: why can’t they be different class instances? Why do genetics all need to share the same class? C++ (messily, admittedly) treats each specific template class type as a separate class.
A Class object used in this fashion is called a type token. You can also use a custom key type. For example, you could have a DatabaseRow type representing a database row (the container), and a generic type Column<T> as its key.
What would be the various key types? Column names somehow? Or each column is a different known type for type safety, but then how do you have two columns of same type?
Luckily, there is a better way to associate a different behavior with each enum constant: declare an abstract apply method in the enum type, and override it with a concrete method for each constant in a constant-specific class body. Such methods are known as constant-specific method implementations:
What you really want is to be forced to choose an overtime pay strategy each time you add an enum constant. Luckily, there is a nice way to achieve this. The idea is to move the overtime pay computation into a private nested enum, and to pass an instance of this strategy enum to the constructor for the PayrollDay enum. The PayrollDay enum then delegates the overtime pay calculation to the strategy enum, eliminating the need for a switch statement or constant-specific method implementation in PayrollDay.
enum PayrollDay { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY(PayType.WEEKEND), SUNDAY(PayType.WEEKEND);
Seems like would’ve been more instructive example not to provide a default constructor and force Strategy selection as indicated in the text.
But this is clever and readable and I like it.
But it also includes other sets for which you know all the possible values at compile time, such as choices on a menu, operation codes, and command line flags. It is not necessary that the set of constants in an enum type stay fixed for all time. The enum feature was specifically designed to allow for binary compatible evolution of enum types.
Recommendations for safely persisting enum types when we recognize the enum set might change over time (if we allow for only expanding and not shrinking list)? I assume there are no guarantees of value consistency across compilations? Or are there if order of declaration doesn’t change? Maybe also if new values are only appended?...
For example, suppose you want to support a category of test that succeeds only if it throws a particular exception. The exception type is essentially a parameter of the test. You could encode the exception type name into the test method name using some elaborate naming pattern, but this would be ugly and fragile
Have used this pattern to make it clear to humans what the test is up to, but absolutely agree not to use naming introspectively to affect runtime behavior. the "test" prefix rule in some test frameworks has made me feel uneasy.
If you can convince yourself that you’ll never want to write a method that accepts only objects with the marking, then you’re probably better off using a marker annotation. If, additionally, the marking is part of a framework that makes heavy use of annotations, then a marker annotation is the clear choice.
I guess. I feel old and still have a bias against annotations where types can be made more explicit via interfaces. Consistency for readability and understandability is a strong argument though. No one should ever be dogmatic about anything. (SWIDT? ;) )
Many method references refer to static methods, but there are four kinds that do not. Two of them are bound and unbound instance method references. In bound references, the receiving object is specified in the method reference. Bound references are similar in nature to static references: the function object takes the same arguments as the referenced method. In unbound references, the receiving object is specified when the function object is applied, via an additional parameter before the method’s declared parameters.
Wow, never done this but I can see where it might be tempting. Starts to result in some magick-looking code (or perhaps just ugly), if you ask me... :/ actually, the inbound case doesn’t seem too bad, unless it’s also accepting arguments in addition to the object that’s the target of the call so that the receiver and the args are all implicit args? Reminds me of the simulated OO straight C dev I did in a class where we defined a bunch of methods whose first argument was always the data struct “handling” the method call...except more hidden.
If the source is a primitive and the result is an object reference, prefix Function with <Src>ToObj, for example DoubleToObjFunction (three variants).
Most of the standard functional interfaces exist only to provide support for primitive types. Don’t be tempted to use basic functional interfaces with boxed primitives instead of primitive functional interfaces. While it works, it violates the advice of Item 61, “prefer primitive types to boxed primitives.” The performance consequences of using boxed primitives for bulk operations can be deadly.
You should seriously consider writing a purpose-built functional interface in preference to using a standard one if you need a functional interface that shares one or more of the following characteristics with Comparator: • It will be commonly used and could benefit from a descriptive name. • It has a strong contract associated with it. • It would benefit from custom default methods.
It is a statement of programmer intent that serves three purposes: it tells readers of the class and its documentation that the interface was designed to enable lambdas; it keeps you honest because the interface won’t compile unless it has exactly one abstract method; and it prevents maintainers from accidentally adding abstract methods to the interface as it evolves. Always annotate your functional interfaces with the @FunctionalInterface annotation.
The easiest way to avoid this problem is not to write overloadings that take different functional interfaces in the same argument position. This is a special case of the advice in Item 52, “use overloading judiciously.”
each time you write a method or constructor, you should think about what restrictions exist on its parameters. You should document these restrictions and enforce them with explicit checks at the beginning of the method body. It is important to get into the habit of doing this. The modest work that it entails will be paid back with interest the first time a validity check fails.
A safe, conservative policy is never to export two overloadings with the same number of parameters. If a method uses varargs, a conservative policy is not to overload it at all, except as described in Item 53.
Use the Javadoc @throws tag to document each exception that a method can throw, but do not use the throws keyword on unchecked exceptions.
One way to ensure that exceptions contain adequate failure-capture information in their detail messages is to require this information in their constructors instead of a string detail message.
A third approach to achieving failure atomicity is to perform the operation on a temporary copy of the object and to replace the contents of the object with the temporary copy once the operation is complete.
Better still, follow the advice in Item 59 and use the class AtomicLong, which is part of java.util.concurrent.atomic. This package provides primitives for lock-free, thread-safe programming on single variables. While volatile provides only the communication effects of synchronization, this package also provides atomicity.
It is acceptable for one thread to modify a data object for a while and then to share it with other threads, synchronizing only the act of sharing the object reference. Other threads can then read the object without further synchronization, so long as it isn’t modified again. Such objects are said to be effectively immutable [Goetz06, 3.5.4]. Transferring such an object reference from one thread to others is called safe publication