Sunday, April 17, 2011

Classes and Interfaces - Part 1

Minimize the accessibility of classes and members
  1. Information hiding (a.k.a. encapsulation) is very important to have a very neat API and to have loosely coupled components.
  2. It is advisable to make a class or member as inaccessible as possible. Top-level classes can be package-private or public. If it is being used by only one other class, make it an inner class.
  3. Member variables/methods of a class can be private, protected, package-private, or public.
  4. Java doesn’t allow overridden methods to have a lower scope than the super class. Also all the methods declared in an interface are assumed to be public only.
  5. Classes with public mutable fields are not thread-safe. In general fields of a class must never be public. A final field with reference to a mutable object has all limitations of a non-final field. Note that a non-zero length array is always mutable. So it should never be made public or its reference should never be returned. To fix this problem use Collections.unmodifiableList(Array), or you can clone the array and then return it.
In public classes, use accessor methods, not public fields
  1. If a class is accessible outside its package, provide public accessor methods. In case it is an immutable field, it can be exposed.
  2. If a class is package-private or a private nested class, though it is highly recommended, you can choose to do otherwise also.
Minimize Mutability
  1. Immutable classes are easier to design, test and are less prone to errors. To make a class immutable, don’t provide any method that modifies the objects internal state. Ensure that the class can’t be extended. Make all the fields of the class private and final. If the class contains references to mutable objects, ensure that the clients can never obtain a reference to those objects.
  2. Immutable classes should always take functional approach by creating a new object and returning it rather than modifying the existing object itself.       Immutable classes are always thread-safe. It should also try reusing the same instances rather than creating new ones. Immutable classes should not have any copy constructor.
  3. Immutable classes can also share their internal referenced objects across objects. E.g. BigInteger internally has an int to represent the sign, and an array to store the number. When you use negate() function, it creates a new BigInteger with a different sign, but will reuse the same array of the caller object for optimization reasons.
  4. One disadvantage of immutable classes is that, it might become a performance overhead as new objects have to be created for every minor change in value of the object. If an operation is performed in multiple steps, it might lead to creation of many immutable objects being created.
  5. Generally immutable classes expose a public mutable companion class to perform multistep operations. E.g. StringBuilder is the mutable companion of String class.
  6. If an immutable class implements Serializable interface and it contains fields referring to mutable objects, you must provide an explicit readObject or readResolve method, or use the ObjectOutputStream.writeUnshared and ObjectInputStream.readUnshared methods, even if the default serialized form is acceptable.
Favour composition over inheritance
  1. Unlike method invocation, inheritance violates encapsulation. Subclasses depend on the implementation of super class for proper functioning. Changes in superclass might break the subclasses.
  2. Create a class called Forwarding class in which each method of the composition object has a corresponding method in the Forwarding class. So it is always better for the Forwarding class to implement the interfaces that the contained class has implemented. It becomes more like Decorator. For any value addition, instead of using inheritance, implement a Forwarding class and add the new value to that class.
  3. Disadvantage: Wrapper classes are not suitable for callbacks where an object passes itself to other objects for subsequent invocations. Because a wrapped object doesn’t know of its wrapper, it passes a reference to itself and callbacks elude the wrapper. This is called a SELF problem.
I have taken these points from the book "Effective Java" which I consider as a MUST READ book for every JAVA developer.

No comments:

Post a Comment