domingo, agosto 21, 2011

Effective Java: Methods Common to All Objects

El capítulo tres de Effective Java explica los métodos que podemos sobrescribir de Object (equals, hashCode, toString, clone y finalize).

En el caso del equals lo más importante es recordar el contrato y las propiedades que este tiene que cumplir lo que provoca que no haya forma de extender una clase instanciable añadiendo un nuevo atributo y seguir cumpliendo el contrato. Para este tipo de situaciones la única solución es optar por la composición en lugar de la herencia (pág 37-41).

El hashCode deberá ser sobrescrito siempre que el equals lo sea y en caso de que este se complicado de calcular podremos optar por usar lazy loading.

El toString debería ser siempre sobrescrito y es aconsejable en según que situaciones explicar claramente en el javadoc del método el formato en el que se devuelve la información. Por ejemplo:
/**

* Returns the string representation of this phone number.
* The string consists of fourteen characters whose format
* is "(XXX) YYY-ZZZZ", where XXX is the area code, YYY is
* the prefix, and ZZZZ is the line number. (Each of the
* capital letters represents a single decimal digit.)
*
* If any of the three parts of this phone number is too small
* to fill up its field, the field is padded with leading zeros.
* For example, if the value of the line number is 123, the last
* four characters of the string representation will be "0123".
*
* Note that there is a single space separating the closing
* parenthesis after the area code from the first digit of the * prefix. */
@Override public String toString() {
return String.format("(%03d) %03d-%04d", areaCode, prefix, lineNumber);
}
En cuanto al clone debemos recordar que si sobrescribimos este método en una clase no-final tendremos que invocar el super.clone(). Otros consejos que da son que las clases inmutables no deberían ofrecer un clone porque generaría otro objecto exactamente igual que el anterior y que no se va a modificar. Y para terminar explica todos los problemas que conlleva este método aconsejando que en ocasiones es más simple ofrecer un copy constructor:
public Yum(Yum yum);
O una copy factory:
public static Yum newInstance(Yum yum);
También hay unas lineas dedicadas al compareTo de la interfaz Comparable y explica el "truco" de devolver la resta cuando comparamos dos números además de explicar que esto no funciona cuando comparamos un números positivo grande y un negativo ya que puede desbordar y devolver un resultado engañoso.

Para terminar finalize no debería ser utilizado.

miércoles, agosto 03, 2011

Effective Java: Creating and destroying objects

En el capítulo dos del libro Effective Java se tratan los temas relacionados con la creación y destrucción de objetos. Es un capítulo bastante básico donde explica que hay que usar Boolean.TRUE en lugar de new Boolean(true) y que nunca hagas un new String("ejemplo").
Podemos destacar dos puntos de este capítulo:
  • A partir de Java 5.0 la mejor forma de crear un singleton es usando un Enum:
public enum Elvis {

INSTANCE;

public void leaveTheBuilding() { ... }
}
  • Cuando tenemos un constructor con muchos parámetros algunos de ellos obligatorios y otros opcionales, la mejor opción es crear un Builder:
// Builder Pattern

public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;

public static class Builder {
// Required parameters
private final int servingSize;
private final int servings;
// Optional parameters - initialized to default values
private int calories = 0;
private int fat = 0;
private int carbohydrate = 0;
private int sodium = 0;

public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}

public Builder calories(int val)
{ calories = val; return this; }
public Builder fat(int val)
{ fat = val; return this; }
public Builder carbohydrate(int val)
{ carbohydrate = val; return this; }
public Builder sodium(int val)
{ sodium = val; return this; }

public NutritionFacts build() {
return new NutritionFacts(this);
}
}

private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
}