Sunday, March 23, 2014

Lo nuevo de Java 8: Interfaces funcionales

En Lo nuevo en Java 8 describimos brevemente las expresiones lambda en esta nueva versión de Java. Es necesario asociar a esas expresiones un tipo para de esta manera satisfacer la comprobación de tipos que el lenguaje utiliza. La interfaces funcionales cumplen ese objetivo, además de poder ser usadas como siempre.
Una interfaz funcional es una interfaz que contiene un único método abstracto, además de algunos métodos implementados. Estos últimos se conocen como métodos de default o métodos de extensión virtual. Las interfaces también pueden contener funciones estáticas, aunque esto último nada tiene que ver con el adjetivo funcional que se les da.
Una expresión lambda encapsula una funcionalidad específica y parece "normal" asignarle a ellas un tipo dado por una interfaz que declara la firma de esa función.
Ya estamos acostumbrados a utilizar una interfaz con un solo método para precisamente manipular la funcionalidad de ese método y poder reutilizarlo. El ejemplo más común es el de las interfaces que definen los "event handlers" en Swing y otros APIs.
Java 8 ha querido que ese idiom siga siendo usado para las expresiones lambda, y entonces bautiza a esa categoría de interfaces como interfaces funcionales.
Con propósitos informativos Java 8 define el tipo anotación correspondiente para indicar la intención de que el tipo interfaz anotada va a ser usado para describir una funcionalidad particular. Esa anotación está definida como:
@Documented
 @Retention(value=RUNTIME)
 @Target(value=TYPE)
public @interface FunctionalInterface
y las instancias de las interfaces funcionales podrán crearse con expresiones lambda, con referencias a métodos y con referencia a constructores. El compilador comprobará si una interfaz anotada con este tipo es realmente una interfaz funcional, aunque permitirá (como debe ser) utilizar cualquier interfaz con el propósito de interfaz funcional si ella es realmente una interfaz funcional aunque no esté anotada con esta anotación.
Como es conocido, existen en el lenguaje muchas interfaces funcionales. Algunas de ellas han estado presente desde la primera versión del lenguaje. java.lang.Runnable, por ejemplo, es una interfaz funcional que incluye sólo un método abstracto void run()

El paquete java.util.function

Este paquete incluye 40+ interfaces funcionales cuya intención es describir funcionalidades "comunes" que usamos en programación. Otros paquetes de "terceros" hacen lo mismo para funciones comunes en sus áreas.
Una de las interfaces funcionales en java.util.function es la interfaz java.util.function.Function. El código fuente simplificado de esta interfaz es:
package java.util.function;
@FunctionalInterface
public interface Function<extends Object, R extends Object> {
    public R apply(T t);
    //Métodos de default y funciones estáticas que por lo pronto no nos interesan
    }
}
Esta interfaz representa una función que recibe un argumento y produce un resultado. El método abstracto  public R apply(T t), una vez implementado será el usado cuando la expresión lambda sea invocada, siguiendo la sintaxis normal de invocación a funciones.
El ejemplo siguiente ilustra el uso de esta interfaz funcional. Se escriben dos métodos estáticos "generales" para ejecutar cualquier función trigonométrica en un caso y alguna operación que modifica una lista en el segundo. En el primer caso usamos lo nuevo de referencias a métodos para referirnos a algunas de las funciones que brinda la clase java.lang.Mathy en el segundo escribimos una expresión lambda muy sencilla "tipada" con esa interfaz funcional: inpt ->{Collections.sort(inpt);return inpt;};
class FunctionDemo {
   
     public static void main(String[] args) {
        // TODO code application logic here
        //Usamos un par de funciones trigonométricas, con referencias a métodos
        double result = FunctionDemo.trigonometric(Math.PI*30/180Math::sin);
        System.out.format("%s%f%s%f\n","sin(",Math.PI*30/180,") = ",result);
        result =FunctionDemo.trigonometric(Math.PI*30/180Math::cos);
        System.out.format("%s%f%s%f\n","cos(",Math.PI*30/180,") = ",result);
        //Usamos la función processingList con una expresión lambda
        Function<List<Integer>List<Integer>> f;
        f =inpt -> {Collections.sort(inpt);return inpt;};
        List<Integer> myList =Arrays.asList(2,4,5,6,18,25,131,4);
        FunctionDemo.processingCollections(myList,f);
        System.out.println(myList);
    }

  static double trigonometric(double value, Function<DoubleDouble> function){

    return function.apply(value);
 
  }
  static <T> List<T>processingCollections(List<T> value, Function<List<T>List<T>> function){
      return function.apply(value);
  }
}
La ejecución del programa anterior produce el siguiente resultado:
sin(0.523599) = 0.500000
cos(0.523599) = 0.866025
[124456131825]
and that´s all folks for today.

Friday, March 21, 2014

Lo "nuevo" de Java 8

Oracle ha liberado recién la nueva versión de Java. En casi 20 años de historia el lenguaje dispone de 8 versiones y de innumerables actualizaciones para cada una de las que han existido. Sigo el lenguaje desde que nació y lo he usado para varias cosas, entre ellas para enseñar programación orientada a objetos cuando el sombrero que llevo puesto es el de profesor.
Escribo entre comillas la palabra nuevo para satisfacer a aquellos que consideran que no hay innovación alguna en las cosas que en la nueva versión se introducen. Casi siempre se cumple eso de que "no hay nada nuevo bajo el sol", pero siempre se recibe son agrado si los que diseñan el lenguaje que usas para ganarte el pan de cada día, incorporan a ese lenguaje las cosas buenas que otros han incorporado. Sucede aunque con menos frecuencia, que también se incorporan cosas malas.
Oracle siempre documenta lo nuevo de Java categorizando en varios rubros lo que incorpora al lenguaje. Voy a tomar esa categorías  para comentar aquí un resumen de lo incorporado y en este post comentaré lo que concierne al lenguaje. En posts subsiguientes me referiré a otras cosas, y en otros que sigan si no canso al lector ilustraré todas esas "novedades" con ejemplos.

Expresiones lambda


Son bastantes los lenguajes que han incorporado las expresiones lambda en su diseño. Lenguajes algo lejanos a la programación funcional como C++ y C# ya lo han hecho y casi todos los lenguajes que hoy se inventan tienen esa construcción como uno de sus requerimientos. Sin entrar mucho en el meollo de toda la teoría que rodea al cálculo lambda, podemos con cierto riesgo decir que una expresión lambda es un bloque de código que puede ser manipulado como una función sin necesidad de atarla a un identificador y que es posible manipularla en el lenguaje como se manipula cualquier otro valor u objeto si así se prefiere entender (funciones como ciudadanos de primera clase). Se usan varios términos para referirse a lo mismo, entre ellos, función anónima, literal función y constante función son de los que más abundan.
De nada serviría dar soporte en el lenguaje a esa construcción si esos bloques no pudieran ser tratados como valores: asignarlos a variables, pasarlos como argumentos a funciones, ser retornados como funciones, etc. Y entre todas esas cosas, la que sobre sale es la de que el lenguaje permita diseñar funciones de orden superior (forma funcional, funcional, functor); funciones que al menos permiten recibir como entrada funciones y devolver funciones como salida. Una vez que se tiene ésto, es posible entonces enriquecer el diseño del programa con los famosos closures o clausuras, a los cuales me referiré en otro post.
Esa necesidad de pasar como argumento un bloque de código a un método de algún objeto siempre ha estado presente en las aplicaciones que se escriben con Java y cualquier otro lenguaje. El ejemplo que casi todo el mundo utiliza para hacer evidente esa necesidad es en el procesamiento de eventos y, en ese contexto casi siempre se toma a Swing como el escenario típico. Desde sus inicios, Java nos ha "obligado" a hacer eso con clases anónimas que implementan alguna interfaz funcional de manera directa o indirecta (indirecta cuando heredamos de alguna clase que ya brinda implementación por default para algunos métodos). Muchos consideran que esa técnica es demasiado verbosa, lo que puede ser cierto en algunos casos y falso en otras situaciones. Lo importante a destacar es que realmente lo que hacemos cuando usamos esa técnica es porque tenemos la necesidad de hacerle llegar al método un bloque de código que el objeto que procesa ese método requiere para completar su trabajo. En esencia, pasar una función o bloque de código que pueda ser tratado con independencia y al cual se le pueda asignar un tipo.
Sigue el ejemplo casi "canónico"

/*Suponemos la existencia del objeto boton y necesitamos añadir código  para procesar el evento de acción que dispara*/
boton.addActionListener(new  ActionListener() {
    public void actionPerformed(ActionEvent event) {
        System.out.println ("Presionaste el botón y puedes ver este mensaje en consola");
    }
});

Como es conocido, ActionListener es una interfaz funcional (la descripción de una acción específica que debe ser implementada por quien quiera usarla). Esa acción o código como queramos llamarla es lo que requiere el objeto boton para realizar su trabajo. Las expresiones lambda llegan en su auxilio y la porción de código anterior, podemos escribirla ahora así:

boton.addActionListener(event -> System.out.println("Presionaste el botón y puedes ver este mensaje en consola");

¿Qué hemos logrado? Varias cosas y lo fundamental es que logramos mayor claridad en nuestra intención, que no es otra que hacerle llegar al objeto el bloque de código que es lo que realmente necesita. Por supuesto que hay otras cosillas en eso que hemos escrito, como la libertad de expresar el parámetro de entrada al bloque de código sin tener que indicar su tipo. Ya era hora que Java incluyera algo de inferencia de tipos, cuando es evidente.

Referencias a métodos


Sucede con bastante frecuencia que las expresiones lambda que escribimos, no hacen otra cosa que llamar a un método existente, bien de instancia o de clase. Podríamos entonces bautizar  esa expresión lambda con el nombre del método que ella invoca, y usar ese nombre para referirnos a ella. Sigue un ejemplo


/*IntPredicate es una interfaz funcional que usa el método test para comprobar la veracidad del predicado*/
interface IntPredicate {
     boolean test(int n);
}

El predicado IntPredicate se escribe para comprobar algunas de las propiedades de los números enteros. Usamos en el ejemplo la que escribimos y no la que ofrece el lenguaje. El propósito es no tener que meternos por lo pronto en los métodos de extensión virtual que esta última tiene.
Escribimos ahora una implementación de IntPredicate para comprobar si el parámetro del método test( int) es impar.

class OddPredicate implements IntPredicate {
    boolean test(int n) {
        return n %2 != 0;
    }
}


Escribimos ahora un filtro para crear una nueva lista que contenga solamente los números que satisfagan el predicado. El código es:

static List filter(IntPredicate, List from) {
    List to = new ArrayList<>();
    from.stream().filter( (item) -> (predicate.test(item) ) ).foreach( (item) -> { to.add(item) } );
    return to;

Escribimos ahora el código para filtrar los números enteros:


IntPredicate myPredicate = new OddPredicate();
List numeros = Arrays.asList(1,2,3,4,5,6,7,8,9);

/* Si no existieran referencias a métodos */
List impares1 =  filter(n -> obj.test(n) ,  numeros);

/*Pero es más "legible" usando referencias a métodos. Nos referimos a ese método con obj::test */
List impares2 = filter( obj::test, numeros);


Otras "novedades" del lenguaje en Java 8 son: los métodos de default o métodos de extensión virtuales, mejoras en las anotaciones, un sistema de inferencia de tipos mejorado y algunas cosillas para usar reflexión sobre los parámetros de los métodos. Lo discuto en el post que seguirá a éste.

Friday, September 7, 2012

Builder vs Constructor ("Buildea" un objeto)

Continúo con el post anterior, describiendo la construcción de objetos mediante un Builder.
El patrón de diseño Builder es uno de los patrones descritos en el libro Design Patterns: Elements of Reusable Object-Oriented Software. Es un patrón creacional que separa el proceso de construcción de un objeto complejo de su representación, de manera que el mismo proceso de construcción pueda utilizarse para diferentes representaciones.
Una variante de este patrón puede utilizarse para resolver problemas con la creación de objetos, como es el problema que hemos descrito del telescoping constructor. La idea es que la clase Estudiante se asocie "fuertemente" con un objeto colaborador, que se encargue del proceso de construcción de los objetos de clase Estudiante, y que el cliente haga uso directo de ese Builder para ordenar la creación de un estudiante, una vez que el Builder tenga todos los "ingredientes" necesarios para hacerlo.
Esa asociación fuerte (una relación de amistad en C++) se logra haciendo que la clase Builder quede anidada dentro de la clase Estudiante y que sea utilizada directamente por el cliente. El método de construcción del Builder tendrá que tener acceso a la privacidad de la clase Extudiante para lograr el objetivo.
El código entonces quedaría de la siguiente forma:
public class Estudiante {
  private final int id;   //Podemos regresar al caso inicial con atributos finales
  private final String nombre;
  private final Calendar fechaDeNacimiento;
  private String carrera;
  private int semestre;
  private  String imss;
  private  String issste;
  private boolean gimnasio;
  private boolean becario;
// Viene ahora el Builder para el Estudiante
  public static class EstudianteBuilder {
    //Parámetros requeridos
    private final int id;
    private final String nombre;
    private final Calendar fechaDeNacimiento;
    private String carrera;
    private int semestre;
    
    // Parámetros opcionales
    private String imss =null;
    private String  issste=null;
    private boolean gimnasio=false;
    private boolean becario=false;
//Constructor del Builder
    public EstudianteBuilder(int id, String nombre, Calendar fechaDeNacimiento, String carrera, int semestre) {
       this.id =id;
       this.nombre = nombre;
       this.fechaDeNacimiento = fechaDeNacimiento;
       this carrera = carrera;
       this.semestre = semestre;
    }
    // Simulando parámetros con nombre
    public EstudianteBuilder imss( String imss) { this.imss = imss; return this; }
    public EstudianteBuilder issste( String issste) {this.issste = issste; return this;}
    public EstudianteBuilder gimnasio(boolean gimnasio) { this.gimnasio= gimnasio; return this;}
    public EstudianteBuilder becario( boolean becario) { this.becario = becario; return this;}
    //El build del Builder
    public Estudiante build(} {
       return new Estudiante(this);
    }
  } // Termina la clase EstudianteBuilder y continúa la clase Estudiante.
  private Estudiante (EstudianteBuilder builder) {
     id = builder.id;
     nombre = builder.nombre;
     fechaDeNacimiento = builder.fechaDeNacimiento;
     carrera = builder.carrera;
     semestre = builder.semestre;
      imss = builder.imss;
      issste = builder.issste;
     gimnasio = builder.gimnasio;
     becario = builder.becario;
  }
} // Fin de la clase Estudiante
La creación de objetos de clase Estudiante se realizará como sigue:
Estudiante.EstudianteBuilder builder = new EstudianteBuilder(12344, "Louis", new Calendar.Builder().setDate(1760,01,01).build(), "Vampirismo", 8);
// Tiene seguro social y es becario. No va al gimnasio ni tiene issste.
Estudiante louis = builder.imss("435678").becario(true).build();
¿Parámetros con nombre? Ahí lo dejo.
Nota al margen: Lo de Buildea es a propósito para generar ira.

Thursday, September 6, 2012

Siguiendo el estilo Java Beans


En el post anterior ( http://ferrocastro.blogspot.mx/2012/09/como-crear-objetos.html) nos referimos al uso de métodos estáticos de fábrica para complementar la creación de objetos por esa vía, en lugar de usar los constructores de la clase. Veamos ahora otros problemas que surgen con la creación de objetos con constructores y cómo pueden solucionarse.
Supongamos que en un sistema de control escolar, modelamos el concepto estudiante con la clase siguiente:
public class Estudiante {
  private final  int id; // El número de matrícula del estudiante
  private final String nombre; // Sus nombres y apellidos
  private final Calendar fechaDeNacimiento;
  private String carrera; // la carrera que cursa
  private int semestre; //el semestre que cursa
  private String imss; // El número de seguridad social del IMSS
  private String issste; // el número del seguro social del ISSSTE
  private boolean gimnasio;// el estudiante hace uso o no del gimnasio
  boolean becario; // el estudiante realiza actividades de becario
  // Constructores para la clase Estudiante
  // Vienen aquí los constructores de la clase
  // Comportamiento asociado al estudiante
  //Vienen aquí los métodos
}
Se nos pueden ocurrir otros atributos, o que algunos de los atributos que hemos añadido pueden encapsularse en otras clases, pero el objetivo no es discutir sobre el buen diseño de los atributos. Lo que queremos destacar, es que de ese conjunto de atributos, algunos será siempre requeridos y otros serán opcionales, y que esa consideración debe tomarse en cuenta para el diseño de los constructores de la clase.
Supongamos (pienso que es una buena suposición) que los valores de los atributos id, nombre, fechaDeNacimiento, carrera y semestre siempre se requieren para crear un objeto de clase Estudiante y que el resto de los atributos pueden tomar valores opcionales. Noten que también he establecido que los atributos id, nombre y fechaDeNacimiento son constantes, por lo que estaremos obligados a inicialziarlos, bien en el momento de su declaración (que no hemos hecho) o en sus constructores que tenemos que hacer.
Es costumbre en un caso como éste, escribir varios constructores para aprovechar esos valores opcionales para algunos de los atributos y no tener que pasarlos como argumentos en el caso que esos valores sean los que se desean para el objeto creado.
Procedemos entonces a armar un telescopio "plegable" y escribimos los siguientes constructores para esa clase:
public class Estudiante {
  //Atributos requeridos
  private final  int id; // El número de matrícula del estudiante
  private final String nombre; // Sus nombres y apellidos
  private final Calendar fechaDeNacimiento;
  private String carrera; // la carrera que cursa
  private int semestre; //el semestre que cursa
  //Atributos opcionales
  private String imss =null; // El número de seguridad social del IMSS
  private  String issste=null; // el número del seguro social del ISSSTE
  private boolean gimnasio=false;// el estudiante hace uso o no del gimnasio
  boolean becario=false; // el estudiante realiza actividades de becario
  // Constructores para la clase Estudiante
  public Estudiante( int id, String nombre, Calendar fechadeNacimiento, String carrera, int semestre) {
     this (id, nombre, fechaDeNacimiento, carrera, semestre, null);
  }
 public Estudiante(int id,String nombre, Calendar fechaDeNacimiento, String carrera, int semestre, String imss) {
    this (id, nombre, fechaDeNacimiento, carrera, semestre, imss, null);
  }
  public Estudiante(int id,String nombre, Calendar fechaDeNacimiento, String carrera, int semestre, String imss,String issste) {
    this (id, nombre, fechaDeNacimiento, carrera, semestre, imss, issste, false);
  }
  public Estudiante(int id,String nombre, Calendar fechaDeNacimiento, String carrera, int semestre, String imss,String issste, boolean gimnasio) {
     this (id, nombre, fechaDeNacimiento, carrera, semestre, imss, issste, gimnasio, false);
  }
  public Estudiante( int id, String nombre, Calendar fechadeNacimiento, String carrera, int semestre, String imss,String issste, boolean gimnasio,  boolean becario) {
    this.id = id;
    this.nombre =nombre;
    this.fechaDeNacimiento = fechaDeNacimiento;
    this.carrera = carrera;
    this.semestre =semestre;
    this.imss =imss;
    this.issste = issste;
    this.gimnasio = gimnasio;
    this.becario = becario;
  }

  // Comportamiento asociado al estudiante
  //Vienen aquí los métodos
}
Llegamos entonces a lo que Joshua Bloh le llama el "telescoping ocnstructor", en ausencia de la técnica de parámetros con nombres. Podrían haberse escrito más constructores para considerar todas las posibles combinaciones de los parámetros opcionales, en lugar de tener que especificar valores opcionales en las llamadas a los constructores, pero con los que ya hemos escrito basta para indicar que seguramente habrán otras soluciones "más elegantes".
Salta a la vista, sobre todo para aquellos que ya conocen la técnica de contenedores que administran objetos y que usan inyección de dependencias, que una solución sería escribir setters para los atributos al estilo de Java Beans. El código siguiente realiza eso aunque no escribimos todos los setters:
public class Estudiante {
  private  int id; // El número de matrícula del estudiante
  private String nombre; // Sus nombres y apellidos
  private Calendar fechaDeNacimiento;
  private String carrera; // la carrera que cursa
  private int semestre; //el semestre que cursa
  private String imss =null;; // El número de seguridad social del IMSS
  private String issste =null; // el número del seguro social del ISSSTE
  private boolean gimnasio = false;// el estudiante hace uso o no del gimnasio
  boolean becario= false; // el estudiante realiza actividades de becario
// Constructor sin argumentos para seguir la convención Java Beans
  public Estudiante () {
  }
//Setters:
  public void setId (int id) { this.id =id;}
  public void setNombre(String nombre) {this.nombre =nombre;}
  public void setFechaDeNacimiento( Calendar fechaDeNacimiento) { this.fechaDeNacimiento = fechaDeNacimiento,}
  public void setCarrera (String carrera) {this.carrera =carrera;}
  public void setSemestre (String semestre) {this.semestre = semestre};
  public setImss(String imss) {this.imss = imss;}
  public setIssste(String issste) {this.issste = issste;}
  public setGimnasio( boolean gimnasio) = {this.gimnasio = gimnasio;}
  public setBecario (boolean becario) {this.becario = becario;}
  // Y así con los setters para el resto de los atributos
  // Comportamiento asociado al estudiante
  //Vienen aquí los métodos
}
Con el diseño anterior, creamos un estudiante de la siguiente forma:
Estudiante geek = new Estudiante();
geek.setId(14327);
geek.setNombre("Juan de los Palotes");
geek.setFechaDeNacimento(Calendar.set(1990,12,31);
geek.carrera("Brujeria avanzada");
geek.semestre(9);
geek.becario(true);
Dos problemas se presentan al seguir la convención de Java Beans: no podemos lograr atributos inmutables (noten que suprimí el final) y el proceso de obtener un objeto de tipo Estudiante "consistente" ocupa más de una sentencia, por lo que entre sentencias tenemos un objeto inconsistente. Cuando trabajamos con un modelo de componentes orientado a contenedores, el contenedor no me "entrega" el objeto hasta que lo tenga totalmente consistente y el grado de consistencia lo establecemos nosotros.
¿Habrá(n) otra(s solución(es)?
Por supuesto, pero la dejamos para el siguiente post.

Sunday, September 2, 2012

¿Cómo crear objetos?


La pregunta que da título a esta entrada, parece tener una respuesta trivial, y en efecto la tiene. No existe otra manera de crear objetos en Java u otros lenguajes similares que no sea a través del operador new del lenguaje. Pero la pregunta se hace interesante si nos detenemos a pensar en qué lugar del código debemos escribir esa creación de objetos, de suerte que un usuario de nuestras clases pueda usar objetos de ella.
En estos días estoy platicando sobre la creación de objetos en uno de los cursos que imparto y me resultó interesante compartir esas pláticas aquí. Siempre habrá alguien que se interese por estas cosas.
Java y otros lenguajes de OOP definen "constructores" para la creación de objetos, haciendo uso del mencionado operador y adoptando una sintaxis para ese fin. Todos sabemos que el constructor es una función ( que se note que no digo método) que lleva el mismo nombre de la clase, que no indica su tipo de retorno ni tan siquiera void (aunque por supuesto "retorna" un objeto creado. El Constructor, como cualquier otra función o método tiene en su firma la lista de parámetros de cualquier tipo, que normalmente recibe aorgumentos relacionados con el estado inicial que queremos para el objeto.
Java permite cosas como lo siguiente:
class Foo {
  int foo;
  int bar;
  Foo (int foo, int bar) {
    this.foo = foo;
    this.bar = bar;
  }
  void Foo(int foo,int bar) {
    this.foo = foo;
    this.bar = bar ;
  }
}
que imagino que nadie escribe (llamar a un método con el nombre de la clase), a no ser que sea para auto confundirse, confundir a los demás (peor aun) o para explicar que es algo que permite el lenguaje como es en este caso.
Java permite sobrecargar el constructor, de manera que podamos tener diferentes versiones para crear los objetos. Algunos lenguajes actuales incluyen la técnica de parámetros con nombres (named parameters) y se dan el lujo de no permitir la sobrecarga de constructores, pero eso es otra historia.
Cuando sobrecargamos a los constructores perseguimos el propósito de indicar a los usuarios de nuestra clase "diferentes" variantes de los objetos que ofrecemos. Por ejemplo, quien escribió la clase BigInteger en el core de Java decidió escribir el siguiente constructor para la clase BigInteger:
BigInteger(int bitLength, Random rnd)
para crear un positivo BigInteger "probablemente primo". Esa aseveración se expresa en la documentación o descripción del API de esa clase.
Dicho constructor establece que probablemente generará un número primo. ¿Cómo saber eso? Pues no hay otra forma que leyendo su documentación. ¿Podría lograrse algo más claro en la sintaxis de ese constructor para indicar que es muy probable que el BigInteger generado sea primo?.
Hay varias respuestas a esa pregunta. Una que quizá se le ocurra a alguien es crear una clase ProbablePrime que extienda a BigInteger y que el constructor de esa clase cree un objeto de clase BigInteger con el constructor anterior. Pero hay otra forma más sencilla y es a la que me quiero referir aquí y que muchos también conocen y es la de darle a la clase BigInteger un método estático de fábrica y bautizar a ese método con un nombre más nemotécnico.
¿Qué tal si esa clase ofreciera un método estático como el siguiente?

static BigInteger probablePrime(int bitLength, Random rnd)
¿Cuál línea del código siguiente sería más "limpia" si lo que quiero es generar un número primo?
BigInteger big1 = new BigInteger(10new Random());
BigInteger big2 = BigInteger.probablePrime(10new Random());
Por supuesto que la segunda línea expresa con claridad la intención del programador y Joshua Bloh en su libro "Effective Java" incluye ese precepto en uno de sus items: "Consider static factory methods instead of constructorswhen creating objects"
Esa NO es la única ventaja del uso de métodos estáticos de fábrica. Hay otras que el autor describe, como por supuesto hay algunas desventajas.
Al usar métodos estáticos de fábrica en lugar de constructores, no estamos obligados a crear un objeto brand new cada vez que invocamos el método. Es una ventaja que aprovechan las clases inmutables que permiten el uso de objetos compartidos. Por ejemplo, las clases wrappers de tipos primitivos usan esa técnica para compartir sus objetos inmutables. Sigue el código de uno de los métodos estáticos de fábrica de la clasejava.lang.Integer para encapsular un tipo primitivo.
static Integer valueOf(int i) {
  assert IntegerCache.high >=127;
  if( i >= IntegerCache.low && i <= IntegerCache.high)
    return IntegerCache.cache[ i + ( -IntegerCache.low) ];
  return new Integer(i);
}
Otra ventaja importante del uso de los métodos estáticos de fábrica es que facilitan el diseño de marcos de trabajo basados en interfaces al permitir que un método estático de fábrica retorne un objeto de clase derivada, permitiendo entonces que el marco de trabajo no tenga que hacer publica las implementaciones de sus interfaces y poder cambiar a voluntad esas implementaciones sin afectar el código del cliente. Claro que para lograr ésto, la clase, además de métodos estáticos de fábrica, tiene que incluir constructores públicos o protegidos para permitir que de ella se extienda. El uso entonces de los constructores o de los métodos estáticos de fábrica depende de la pericia y experiencia del programador.
El uso de métodos estáticos de fábrica también se aprovecha para diseñar clases no instanciables por el cliente (clases con constructores privados) que devuelven implementaciones propias del marco de trabajo. Es el caso por ejemplo de la clase java.util.Collectionsque incluye métodos estáticos de fábrica para obtener colecciones. Por ejemplo, el método estático de fábrica siguiente:
public static<T>  Set<T> singleton(T o)
{
  return new SingletonSet<>(o);
}
La clase SingletonSet "no es conocida por el cliente". Es una clase anidada privada de la clase java.util.Collections.
Cuando se usan métodos estáticos de fábrica, es necesario una buena selección de sus nombres. Una de las desventajas de esta técnica es que precisamente las firmas de los métodos estáticos de fábrica no se distinguen de otros métodos estáticos, algo que no sucede con los constructores. El bautizo de métodos es relevante. Robert Martin le dedica un capítulo a ese tópico en su libor "Celan Code"
Otras cosas interesantes con respecto a la creación de objetos
Hay otras cosas interesantes para resolver de manera más elegante algunos pequeños problemas cuando tenemos la necesidad de escribir una clase que requiere de muchos parámetros en su constructor y algunos de esos parámetros siempre se requieren y otros pueden tener valores opcionales. Al resolverlo por la vía "normal" se presenta el efecto "telescopio" en los constructores y existen algunas técnicas para escribir con mayor limpieza, pero ya este post se alargó demasiado y lo dejo para el siguiente.