Sunday 20 September 2009

Item 28 - Use Bounded Wildcards to Increase API Flexibility

In this item Josh introduces the usage of bounded wildcards. Wildcards were introduced in their unbounded form in Item 23 as a type-safe alternative to raw types for use when you do not know or care what the actual type parameter: List<?> represents a list of some arbitrary type, with greater type safety than List<Object>.

Bounded wildcards allow you to specify a type parameter as an arbitrary subtype or supertype of a specified class. So, List<?extends E> represents a list of some subtype of E, and List<? super E> represents a list of somesupertype of E. (Note that the subtype relation is defined so that Number is a subtype of itself.)

As noted in Item 25, generic types are invariant. Given two type B and
D where D is a subtype B, List<D> is not a subtype of List<B>. This can give rise to unfortunate restrictions. Considering the Stack class previously used as an example - you can invoke push(intVal) on a Stack<Number> where intVal is an Integer, because Integer is a subtype of Number. You can also store the return value of pop() in an Object, because Object is asupertype of Number.

However, extending the Stack class to include methods to push and pop sequences of elements runs into a snag:

public void pushAll(Iterable<E> src) {...}
public void popAll(Collection<E> dst) {...}

These won't do; while they compile, they will only work with collections which exactly match the stack's element type. With our Stack<Number>, passing a sequence of Integers topopAll() will fail, as will passing a sequence of Objects to popAll(). The solution is to use bounded wildcards:

public void pushAll(Iterable<? extends E> src) {...}
public void popAll(Collection<? super E> dst) {...}

The pushAll() method can now be passed a sequence of any type which is
a subtype of the stack's type parameter. Likewise, the argument to pushAll(), which is effectively an out parameter, may now be a sequence of any type which is a supertype of the stack's type parameter. In other words, for a Stack<Number>, you can pop sequences of Integers, and push sequences of Objects.

Josh introduces a mnemonic to remember which type of wildcard type to use: PECS, standing for Producer-Extends, Consumer-Super. This is not the most snappy and memorable mnemonic I've ever heard, but if it helps people then fine. I prefer the Get-Put Principle which Josh refers to but does not quote:

"Use an extends wildcard when you only get values out of a structure, use a super wildcard when you only put values into a structure, and don't use a wildcard when you do both."

Note that popAll() defines a parameter rather than a return type. This
allows the wildcard types to be as unobtrusive to the user of the class as possible. If a wildcard type was used as a return type, users of the class would be used to wildcard types in their own code.

After demonstrating the use of wildcard types with some of the methods from previous items, Josh rounds off with a neat little technique - though it does not relate specifically to boundedwildcards . Using the example of a swap() method, he demonstrates that the method could be declared using an unbounded type parameter or an unboundedwildcard. The wildcard version clearly presents a cleaner, simpler interface and so should be used in a public API. However the implementation of the type parameter version is much more straightforward. Happily the tension here can be resolved by making the type parameter version a private helper method, to which the public method forwards, with thewildcard type being captured in the process.

Ewan Milne

Item 27: Favour Generic Methods

Basically, in this item Joshua is telling us that as well as using generic types, we should make our methods generic too. So instead of:

public static Set union(Set s1, Set s2)
{
Set result = new HashSet(s1);
result.addAll(s2);
return result;
}

We should use generics:

public static <E> union(Set<E> s1, Set<E> s2)
{
Set<E> result = new HashSet<E>(s1);
result.addAll(s2);
return result;
}

because the later example provides us with type safety. The compiler uses type inference to work out the return type. All very straight forward and makes complete sense. Joshua could have left it there, but he goes on to show how we could write factory method that enables us to create a generic type and only specify the generic parameters on one side of the expression:

public static <K,V> HashMap<K,V> newHashMap()
{
return new HashMap<K,V>();
}

HashMap<K,V> anagrams = new HashMap();

Even as a C++ programmer who desperately misses typedef, I can't see myself doing this. Just think how many of these static functions you'd need to write to handle all the constructor variations for each type.

Joshua continues with two other examples, but I think the point is well and truly driven home by now. Use generic functions to compliment your generic types. Less code, more software.

Paul Grenyer

Item 26: Favor generic types

Continuing the series on generics, this item gives advice on writing your own generic types. As an example, we are guided through the process of generifying a Stack class. Along the way, we come across a couple of obstacles, and this item suggests solutions to these.

The original Stack had the following constructor:

public Stack() {
elements = new Object[DEFAULT_INITIAL_CAPACITY];
}

When developing the generic version Stack<E> we may think we can write
this constructor:

public Stack() {
elements = new E[DEFAULT_INITIAL_CAPACITY];
}

Unfortunately, this fails to compile because the new operator can't deal with arrays of generic types. So instead we can create an array of Object and cast it to the generic type:

elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];

Instead of an error, we get an "unchecked cast" warning. This normally indicates that what we are doing is not type-safe. But in our class, the array that we create is private, so we know every use of it, and can figure out that what we are doing is safe. We can therefore use a @SuppressWarnings("unchecked") annotation.

Alternatively, we can keep 'elements' as an Object[] array, and then cast every reference of 'elements' where a generic type is required:

E result = (E) elements[--size];

This again emits an unchecked cast warning, which we can also suppress.

Klitos Kyriacou

Item 25: Prefer lists to arrays

The main point put forward in this item is that one should prefer Lists to arrays in certain situations. Specifically, it highlights some of the problems one can see when mixing use of Lists and Java arrays, and suggests that using Lists exclusively is the best way to avoid these problems. So, this item isn’t about other aspects of arrays vs. Lists such as being of fixed length vs. coping with addition etc., as one might have guessed from the headline.

The author points out a couple of major differences between arrays and generic Lists (List<T>). First of all, arrays are covariant, whereas Lists are not. So, String[] is a subtype of Object[], whereas List<String> is not a subtype of List<Object>. The latter aspect of generic types may seem counter-intuitive at first, but the author explains that this can be seen as a benefit: it is safer as it avoids cases where objects of the wrong type can be inserted into the generic collection. (Templated types in C++ are also not covariant)

The other major difference is that arrays are reified, whereas generic collections are not. This means that arrays have information at runtime about the type of their contained items whereas generic collections don’t. Another term for describing this aspect of generic types is "type erasure", as mentioned elsewhere on this list. I guess it’s less obvious if the type erasure of generic collections is an advantage, I’m sure many would say it’s definitely not. But the reason things were implemented in this way is to allow generic collections in generics-aware Java code to interoperate with older code. Personally, I think that was a sensible choice; while limiting some aspects of generics, it provided an easy transition path and allows mixing older and newer code. A clean break upgrade sounds like it would have been very painful and would presumably have left a lot of organisations stuck in the pre-Java 1.5 world.

These differences between arrays and generic collections (Lists discussed in particular) lead to some problems when mixing use of the two. For example, it’s not possible to create typed arrays from generic types in a statement like this inside a generic method or class:

List<E> list = ...
E[] snapshot = list.toArray();

You can make this compile using casts but these are not safe and will produce warnings:

E[] snapshot = (E[])list.toArray();

The reason it’s not safe is that at runtime, there is no type information about the list, hence the created array is basically an Object[].

The author suggests avoiding mixing arrays and Lists in this way by only using Lists. Which makes sense in some cases, but I’m sure there are cases where the advantages of arrays (speed?) mean that they will be preferable, or there’s legacy code or other code outside a developer’s control that uses arrays. In which case, one would have to deal with the resulting complications.

Jan Stette

Item 24: Eliminate unchecked warnings

This item discusses the new unchecked warnings introduced into Java to support generics. Java still supports the old style code without generics to be compatible with existing code. But new development should use generics. The unchecked warnings inform the developer that the code might be using generics that is not typesafe. So this item recommends that all unchecked warnings are eliminated by using generics properly. Basicly listen and act to the messages from the compiler. This might be challenging but the code will be better for it.

If the developer believes that the error can't be removed and that the code is type safe, then the annotation @SuppressWarnings("unchecked") can be used. This will tell the compiler not to report the warning. This should be used on the smallest scope as possible. So other unchecked warnings are not suppressed unnecessarily.

If @SuppressWarnings("unchecked") is used but the code is not type safe, the code could generate a ClassCastException at runtime. So the developer must prove that the code is type safe and a comment should inform others why the annotation was used and why it is typesafe.

Timothy Wright

Item 23: Don't use raw types in new code

This is the first item of a completely new chapter in the Effective Java book 2nd edition about Generics.

The next 6 Items (23…29) deal with rules about using Generics in the right way. Generics were added to Java in Release 1.5. Essentially they allow a type or method to operate on objects of various types while providing compile-time type safety.

IIRC (this is not in the book ;-) the addition of Generics happened in 2004, and the people involved were Gilad Bracha, Martin Odersky and a few others. They wrote 2 compilers, the first was called "Pizza" and added three new features to the Java language: Generics (which is the topic here), Function pointers (first class functions ;-) and Class cases and pattern matching (algebraic types). The second compiler was called "GJ" (generic Java) and "only" contained the generic part and finally made it into the Java language and the JDK 1.5 (AFAIC remember). Some additional interesting (IMO) remarks about pizza and "GJ" at the end of this Item 23 summary. :-)

But now onto the topic: With Generics you use the compiler to "force" e.g. collections containing only a specific type. So we catch bugs earlier (at compile time instead of runtime) which is a good thing, see the code example. However there is no free lunch (™) so the rules in the book try to maximize the benefits (safety and compact code) and minimize the drawbacks (e.g. compiler warning).

// no generics
List ng = new ArrayList();
ng.add("one");
Integer i = (Integer)ng.get(0); // run time error, crash boom bang aehm
ClassCastException

// generics
List<String> g = new ArrayList();
g.add("one");
Integer i = g.get(0); // compile time error
String s = g.get(0); // correct version, no cast necessary

Joshua starts with introducing some terms from the generics are (Type, Actual/Format type Parameter, (Un)Bounded wildcard type, Raw type etc…). Please refer to table (unamed ;-) on page 115 in the book for further information, examples and corresponding Generics related Items.

Generics in Java should not be compared or confused with Templates in C++ (though they have the same angle-bracketed list of actual type parameters). While in C++ the compiler really generates new classes and code (and even can compute new things…) in Java the compiler "just" inserts casts for you automatically. Furthermore, the Java run-time environment does not even know about parameterized types/generics, because type information is validated at compile-time and erased from the compiled code (so it is backward compatible which was one major requirement when designing this language feature). I think exactly this is also the reason that there is this beast called "raw type", which is the name of the generic type WITHOUT any actual type parameters (so the raw type looks like before generics were added to the Java platform at all). example: generic: List<E> and raw type: List.

Of course using "raw type" is like programming as generics would not exist, and you get all the drawback of pre JDK 1.5 time (casting object out of collections, getting run time ClassCastExceptions, tedious debugging etc) so DON’T DO IT (this is the rule). Raw types are just there because of compatibility (migration compatibility)

Fortunately the javac compiler with option -Xlint gives you a warning when using raw types, so this minds you of this Item 23 when writing new code. e.g. "unchecked call to add(E) as a member of the raw type java.util.List" I think a small hint in the book that Item 23 IS already implemented in the std javac compiler with -Xlint would be good (you need no findbugs or other checker here).

To create a collection for arbitrary type you better use List<Object> (which
does "type checking") instead of the raw type List (which has no type checking at all). Also worth to know: there are subtyping rules for generics.

But instead of using raw types (bad) or List<Object> (a bit better), a safe and elegant alternative is to use "unbounded wildcard types" (which means List<?>). The question mark tells "I do not care or know what the actual type parameter is", but please make it type safe, I do not want to use raw types. If you need to specify some constraints about the actual type parameter, then <?> maybe too open but you can use generic methods (Item 27) or "bounded wildcard types" (Item 28) e.g. List<? extends E>, so you can enforce the actual type parameter "?" should be a subtype of E.

Because the generic type information is erased at runtime (you can check this, the javac compiler really JUST inserts the relevant casts, nothing more) we have two exceptions (or better term deviation ? ;-) where raw type actually HAVE to be used:

1. In class literals raw types must be used.
2. instanceof operator cannot query the generics type information because it was erased (not available at runtime). Hence it is useless and adding a <?> does not buy anything. Fortunately -Xlint obeys this and does not raise any noise because of using raw types here (correctly).

So summary: raw types exist because of backward compatibility. Use them only in class literals and instanceof operator. At all other locations use generics to get type errors at compile time instead of ClassCastExceptions at runtime. (A possible drawback is that you get more (or lots of) unchecked warnings which is the topic of the next Item 24).

More things (unfortunately not in the book) but IMO definetly worth to remember or know:

1. TypeErasue is happening in Java Generics. I would prefer to hear this term already in Item 23 or in the forward of Chapter 5. It is only metioned as "erasure" somewhere in Item 25. Please use "TypeErasure" Generics are "only" checked at compile-time for type correctness. The generic type information is then removed. So taking our example from the beginning the following is always true:

List<String> ls = new ArrayList<String>();
List<Integer> li = new ArrayList<Integer>();
if (ls.getClass() == li.getClass()) // evaluates to true


2. Because there is only one copy of a generic class, static variables are shared among all the instances of exactly that class, regardless of their type parameter. Effect: the type parameter cannot be used in the declaration of static variables or in static methods, e.g. the following code does not compile

class GenericLimits<T> {
private static List<T> ts; //does not compile
private static void onlyOneClass(List<T> lt) { //does not compile
}
}

3. only Reference types are accepted as types in Java Generics. We can not
use a base type like int. The following code does not compile:

List<int> li = new ArrayList<int>(); //does not compile

Yet again this was legal in the Pizza compiler.

now happy discussing, :-)

Bernhard Merkle

Item 22: Favor static member classes over nonstatic

This item covers a little more than its title suggests. As well as static and non-static member classes, it also covers anonymous classes (a favourite of mine) and local classes.

Syntactically the only difference between static and nonstatic member (nested) classes is that static members classes have the modifier static in their declarations. In short, an instance of a nonstatic member class can only exist in conjunction with an instance of its outer class. An instance of a static class can exist on its own without an instance of the outer class. If you declare a member class that does not require access to an enclosing instance, always make it a static class by putting the static modifier in its declaration.

The item doesn't make a particularly strong argument for favouring static classes over nonstatic classes. In fact the only arguments seem to be that you can use a nonstatic nested class instance without an outer class instance and that nonstatic nested classes take up more space and take longer to create than static classes due to the reference to their outer class instance.

Paul Grenyer

Item 21: Use function objects to represent strategies

This item describes Java's idioms for implementing the Strategy pattern. As Java doesn't support raw function pointers, a function object is an instance of a class whose main (often only) purpose is to implement a single function. A string comparison function is given as an example, one which compares strings by their length:

class StringLengthComparator {
public int compare(String s1, String s2) {...}
}

An object of this class can be passed to a function that sorts strings, and the client uses this comparator object to specify exactly how the strings are to be ordered.

When I first read this far, I thought, this function doesn't need a "this" pointer, so why not make it static? The answer, of course, is that static functions cannot be overridden in Java. Despite wasting CPU cycles passing an unused "this" reference, it makes sense to make it polymorphic. The idiom is to specify an interface containing the desired name of the method to be overridden. For example, java.util.Arrays.sort() takes an array of a template type T and a strategy interface defined like this:

public interface Comparator {
int compare(T t1, T t2);
}

To use such an interface, a client application has the following choices:

1. Define a class that implements the interface, and pass an instance of that class. Typically, strategy classes are stateless, and therefore the class should be a singleton.

2. Use an anonymous class. I see this quite a lot in GUI applications when passing event handlers, etc, where typically the instance of the anonymous class is created at the point where it is passed to a method. However, if this is done in a loop, a new object is created on the stack each time, so consider assigning this object to a variable and reusing that in the loop.

3. Use one of a set of strategy objects that you have predefined in a utility class. Such a utility class contains a number of inner classes and public static final instances of these. The inner classes can be anonymous. If they are named, they can be made private because the users are only interested in instances, not the classes themselves. The inner classes need to be named if they are to implement more than
just the strategy interface (for example, Serializable.) An example of
such a pattern is the Comparator instance CASE_INSENSITIVE_ORDER
exported by the String class.

I found everything in this item made sense, with no major surprises. What does everybody else think?

Klitos Kyriacou

Sunday 22 February 2009

Item 20: Prefer class hierarchies to tagged classes

Quite a straightforward one, this item, and applicable to all object-oriented programming languages. This explains the very basic use case for polymorphism: not using a type field or tag in a class to mark it for special behaviour, but using a class hierarchy instead. The disadvantages of tagged classes are well known: brittleness in case of change, inability to change behaviour without changing the tagged class itself and cluttered code. On the other hand, class hierarchies can often reflect a natural hierarchy of types in the application domain, leading to a closer match between code and the problem domain it's describing.

I don't know if there's much more to say about this one. But it does bring me straight back to when I did my very first C++, reading the Deitel & Deitel book while at university. I'm pretty sure they used the exact same example, with a hierarchy of shapes, methods to calculate area and all. A very neat idea that's useful in many circumstances, but shouldn't be overused (as discussed in item 16).

Jan Stette

Wednesday 18 February 2009

Item 19: Use Interfaces only to define types

This item is a short one but I found it confusing, maybe because I am a java novice.

The interface defines a type for its clients. Nothing else should be used. The main topic is using interfaces with static final fields that define constants, a so called constant interface. It should not be used because it pollutes the client with implementation details. The constant interface infects the hierarchy of a client class and can never be removed in the future. It could break binary compatibility.

So if I have a class that uses an interface with constants and the class is not final, then I could not remove this interface even if I don't need it? Is this because an inherited class could use it and I don't want to break existing code. Or because I can't because it would break because of the compiled class files? The binary compatibility?

When constants are needed, then they should be defined in a class or interface that they are strongly related to. They either should be a enum type or defined in a concrete utility class that can't be instantiated. Does this mean that the enum or the utility class is really outside the interface? It seems to me that the enum class or utility class must be defined outside the interface and part of the public face of the package. It is a little confusing. It would be nice to have a complete example. Only an example of the utility class is provided.

Use static import facility introduced in 1.5 to include constants from an utility class without the qualifiers.

This item continues the advice from the previous items. But it seems to only concentrate on constants and how to handle them. Maybe this item could be called "How to define constants with interfaces" or something like it.

Timothy Wright

Saturday 14 February 2009

Item18: Prefer interfaces to abstract classes

For defining types that permits multiple implemetations you have two choices (with their pros):
  • interfaces: support multiple inheritance

  • abstract classes: can contain implementations
Usecases which prefer interfaces:
  • retrofitting existing classes (example of the JDK, Comparable Interface introduction)

  • defining mixins (mixin is not a form of specialization but is rather a means to collect functionality), e.g. again the Comparable Interface is a good example for that…

  • Construction of nonhierarchical type frameworks

  • Functionality enhancements via wrapper classes (see Item 16)
Yet interfaces can not contain method implementations (e.g. default implementation or assistance/helper methods for implemetors of this interface). However exactly in this case a abstract class (a skeleton implementation for the interface) comes into play ;-). By convention they have a common "Abstract" prefix in the classname (e.g. AbstractXYZ for Interface XYZ). The idea is to use (and reuse) the abstract class better as implementation helper for an interface rather than as a type definition. Weaving the abstract skeleton class into the concrete class can be done via inheritance of via delegation. Note however that there is no private inheritance statement in Java (C++ there IS private inheritance and it communicates the intention very obvious as implementation inheritance). Does anybody know why there is no private inheritance in Java ?

As abstract skeleton classes are ideal candidates for inheritance (refinement of the default helper implementation) we should document the class as described in Item 17.

One special case of a skeleton class is the "Simple" Implementation which is a non-abstract helper class with a common "Simple" prefix. However I am unsure if this is a common prefix or convention. Even in the JDK source I only found .SimpleEntry and .SimpleImmutableEntry as Simple inner implemetation classes. Does anybody know if this is a common convention and often used ? My impression is that AbstractXYZ means the skeleton approach is used much more often.

Usecases which prefer abstract classes (as type definition)
  • it is far easier to evolve and abstract class than an interface.
    You can simply add the new method with a default implementation and all classes which inherit from that class (and get recompiled ! ;-) will inherit that default implementation. Impossible with interfaces.

  • Workaround for interfaces would be to add the method declaration to the interface and a default method implementation to the skeleton class, however classes NOT inheriting from that skeleton class will break.
However the topic with evolving interfaces and abstract classes or better evolving API in general is much more complicated as the following article shows: (which IIRC mentioned also in another Item recently)

http://wiki.eclipse.org/index.php/Evolving_Java-based_APIs

http://wiki.eclipse.org/Evolving_Java-based_APIs_2

Josh also mentioned that changing an interface (once it is released) is nearly impossible. Well I think everybody, who has clients which are using your written software (programming against your API) has learned this is some way (maybe a painful) way. So get it right in the first place. But how to do this ? I recently read a book "Framework Design Guidelines" (okay from the other side, M$ ;-) but some tips about "how to create and especially test with Clients how good and understandable it the API" are really worth to read IMO.

http://www.amazon.com/Framework-Design-Guidelines-Conventions-Development/dp/0321246756

Bernhard Merkle

Saturday 17 January 2009

Item 17: Design and document for inheritance or else prohibit it

This item is directly related to its predecessor, continuing the discussion of the issues around inheritance. In common with the previous item, and as pointed out by Jan in the excellent summary, the topic under discussion is fairly common and well-known in the OO community. The advice is no less useful for that, though I have my reservations about the item. It is useful to bear in mind that in this pair of items the author isfocusing on inheritance from ordinary concrete classes across package boundaries.

The item begins by describing how to design, and in particular how to document, a class for inheritance. I would have preferred a little more emphasis upfront on the wisdom (or otherwise...) of inheriting from a concrete class. Although the item concludes that designing a class for inheritance places substantial limitations on the class, and does make the recommendation to prohibit subclassing in classes that are not designed and documented to be safely subclassed, I would favour a stronger message here. Prohibit Inheritance By Default, perhaps (see item 33 of Meyers' More Effective C++: Make non-leaf classes abstract). The author notes that his advice as it stands may be somewhat controversial, so I get the feeling he is pulling his punches.

One could suggest that any programmers who have grown accustomed to subclassing ordinary concrete classes would benefit from such a punch - just to knock some sense into them, of course. Two simple methods of prohibiting subclassing are mentioned: declaring the class final, or keeping the constructors private or package-private. As the latter approach was discussed in a previous item, it is reasonable that this section is brief, however it does add to the feeling that this important advice is not given enough weight.

Flipping back to the start of the item, design and documentation for inheritance is discussed. Four main guidelines are presented. Firstly, if a class may be inherited from, then the effects of overriding any method should be documented. For each public or protected method, an indication of which overridable methods are invoked, and how, should be given; in other words, the classes self-use of overridable methods should be publicly documented. Reinforcing the message that inheritance violates encapsulation, this leads to the unfortunate result of API documentation describing implementation details - not just what the method does, but also how it does it. Not mentioned in the book is the dependency this introduces on keeping the documentation in step with the code.

The author also covers the idiom of providing hook protected methods for derived classes to override, allowing specialisation of certain steps within an overall task. Again, I feel uncomfortable about this as general advice; while the example given sounds reasonable, I'm not convinced about the universal applicability of this practice. He goes on to make the sensible suggestion that the best way of testing your class's suitability for inheritance is to write some subclasses. Clearly, this will help smoke out any issues in your design.

Finally, the straightforward guidance that constructors must not invoke overridable methods (and neither should constructors-like methods such as clone() and readObject()). This is a bad idea for the same underlying reason as calling virtual functions in a C++ constructor is, although the two languages differ in their behaviour (see item 9 of Effective C++ for the C++ angle), but the common message is that calling down to parts of an object that have not yet been initialized is inherently dangerous. This at least is indisputable, so in this case I will not take issue with the author.

Ewan Milne

Item 16: favour composition over inheritance

In this item, the author discusses why it's often preferable to use composition instead of inheritance when extending the functionality of classes. He gives a number of reasons why this is, for example by arguing that classes that inherit from base classes and override methods in these are much more tightly coupled to these classes than ones just using the public interface of the base classes. An example that attempts to provide counts for number of inserts into a HashSet is given. For this example, forwarding or wrapper classes are presented as an alternative based on composition.

A couple of examples from the JDK are given that are examples of bad usage of inheritance where composition would have been much preferable: Stack extending Vector, and Properties extending Hashtable. In these example, the extended classes end up with a very fat interface that includes the full interface of the base classes even though some methods don't make sense for the derived class and shouldn't be used. I've come across the first one of these myself and been very surprised about it, it really is a bit of an abomination. For a start, there is no Stack interface, as there is for Map, Set, List etc. Also, it means that with a Java Stack, you can do very un-stack-like operations like inserting into the middle of it etc. And, once the class has been published with this API, it becomes impossible to change it to a different implementation of Stack of course, without extending it from Vector, as client code may depend on the Vector+Stack API that was published.

This advice is definitely useful, and probably well known to anyone who's worked with an OO language for any length of time. Over-use of inheritance is a typical beginner's mistake, I find, when it comes to OO design. I certainly made those mistakes when I started doing OO (in C++). I guess it's partly because of how OO is taught; inheritance and polymorphism are usually a major topic in any OO course. Also, I guess it's such a neat idea when you first come across it that it's tempting to try to fit every design problem into an inheritance hierarchy.

I think there are other reasons as well for preferring composition over inheritance that Bloch doesn't get much into in this item. A design using composition is much more flexible, for a start the relationships between objects can be changed at runtime (actually, he does mention that in his wrapper example). Also, testability is a major advantage. Classes interacting through interfaces can much more easily be used in isolation and tested with the help of stubs or mock classes. And, I think it's easier to provide simpler, cleaner classes that each have a clearly defined responsibility when they are completely separate. Anyone who's ever tried to debug a deep class hierarchy when methods override methods that override methods... knows how messy that can get.

Jan Stette

Item 15: Minimize mutability

Basic rule of this Item: If it is possible: make your class immutable or at least minimize mutability as much as possible because this saves you a lot of trouble.

This Item covers several aspects and effects of mutability, but first the (five) basic rules for immutability:
  • no methods for object state modification / mutatbility
  • ensure that the class can not be extended (either make it final or make all ctors (package)-private and offer static factories, see Item1)
  • - make all field final and private (note, this expresses your intend, however it is still possible to mutate them under certain circumstances)
  • ensure exclusive access to any mutable components
One aspect of immutability is also the functional approach. Returning the result of applying a function and not modifying the operand is one of the key principles in functional programming. FP saves a lot of trouble when it comes to multiple thread, processes, CPUs of whatever granularity you want to name it. I recently learned a functional language and it is really a good experience for every "proecdureal" or "imperative" programmer. Of course for old LISP programmers this is not new stuff, so you are not excited about Erlang, Haskell et all. :-)

Immutable classes (ICs) have several properties which a appreciated:
  • simple (or trivial ? ;-)
  • thread-save out of the box
  • and hence can be shared freely
  • are building blocks for other classes
  • and the JDK has several classes as examples like: String, BigInteger, BigDecimal etc.
On the downside:
  • they should (or have to) be small because...
  • each distinct value requires a separate object
  • in multistep operations you can generate lots of unwanted temporaries
  • somewhere in a program, state changes have to happen, or your program will be very trivial ;-)
Unfortunately there is no annotation (see Item 35 later) or even a simple naming convention to distinguish or mark immutable classes. The only thing is you can hope and read the javadoc for the class. IMO it would make sense to have a standard annotation for immutable classes, despite the fact that the java compiler/annotation checker can not check everything automatically (annotations have some serious limitations) but it would be better than nothing, means no obvious hint (which is the current state in the JDK). I am certain that this would avoid the well known problem that Java Beginners are using Strings to operate with Strings, instead of using StringBuilder or StringBuffer.

IMO a possible standard annotation could check for
  • antipatterns in ICs, e.g. the shall not have a clong method or cctor.
  • ICs shall follow the 5 rules i mentioned in the beginning
  • if your IC has a reference to a mutable object, you have to obey
    serializability (see Item 76)
Regarding the JDK immutable classes there are also other problems, e.g.
  • e.g. String provides a copy constructor but ICs should not have a clone method or cctor.
  • java.util.Date and java.awt.Point should be ICs but are not.
  • String and BigInteger are not so small objects
  • The classes in the JDK can NOT be fixed to be ICs because this would break (binary) backward compatiblity (see my link on the eclipse homepage which describes what you can change and what not, I mentioned this in Item 13)
Josh also outlines some techniques to write performant ICs e.g.:
  • provide a mutable "companion class" for a IC which enables and optimitzes multistep operations This mutable "companion class" can be package-private or package-public. (e.g. BigInteger, String, and their coresponding companions)
and he also shows an alternative approach to make a class "final" e.g.:
  • hide all class ctors and make them private and only offer public static factories (valueOf).
Static factories have several advantages over "normal" ctors, also discussed in Item 1 and in this Item. Unfortunately, AFAIK packages can always be extended by adding other classes (there in no final for packages) so this works only if the clients are outside the packages in question.

My impression of this chapter:
  • pure ICs should be preferred and used if possible
  • try to write ICs and hide mutability in companion classes (which are implementation details and shall not be used by clients)
  • always try to minimized mutability in "normal API classes".
  • there are some defects in JDK ICs as the Java implementors learned also about immutability
  • a standard annotation would a.) communicate the intend forimmutability b.) help us the check the esay cases/rules at least

Bernhard Merkle