Ribe Software con pasión

Enero 14, 2008

Java podría ser mejor

En los últimos días y semanas he leído artículos “contra” Java y estoy de acuerdo con ellos en la mayoría de sus argumentos. Creo que Java no es la mejor elección para todo y que Java no debería ser el único lenguaje enseñado en las universidades.

En este artículo voy a hablar de aquellas cosas en las que en mi opinión Java podría ser mejor. Creo que a veces Java es demasiado “verbose” (hay que escribir bastante código para tareas sencillas). Esto se debe a razones de sintaxis y también a métodos que en mi opinión faltan en la API estándar. Estos son algunos métodos que en mi opinión deberían estar incluídos en el API.

Collections

No hay una forma directa de añadir todos los valores de un array a una colección. Puedes añadir todos los elementos de una colección a otra con addAll(Collection<? extends T&gt ;) , pero no existe la manera de añadir todos los elementos de un array con un sólo método. No existe un método addAll(T[]) method. Se puede usar un truco para no iterar el array:

        List<String> list = new ArrayList<String>();         String[] array = { "foo", "bar" };         list.addAll(Arrays.asList(array));

Pero esto no es óptimo, por supuesto.

Otra cosa con la que he tenido que lidiar es porqué java.util.Properties no implementa Map<String, String> en vez de Map<Object, Object>. Por ejemplo, si tienes un Map<String, String> y quieres añadir todo el contenido de un objeto Properties no puedes usar el siguiente código:

        Map<String, String> map = new HashMap<String, String>();         map.putAll(System.getProperties());

Podrías usarlo si la clase Properties implementase Map<String, String>

Otra cuestión se cómo convertir colecciones en arrays. Puedes usar el método toArray(), pero tienes que pasar un array vacío como argumento.

        List<String> list = new ArrayList<String>();         list.add("foo");         list.add("bar");         String[] array = list.toArray(new String[0]);

Input / output

Leer de la entrada estándar (System.in) siempre ha sido una tarea tediosa en Java. No existe un método System.in.readLine() porque System.in es un InputStream. Tienes que crear un BufferedReader para leer líneas de la entrada estándar. Me encantaría tener algo como Console.ReadLine() (en C#). Bueno, en Java5 puedes usar un scanner. Pero… ¡hasta Java5 había que escribir mucho código! Este es un ejemplo de aplicación echo. implementada con la nueva clase java.util.Scanner:

        Scanner b = new Scanner(System.in);

        String line = null;

        System.out.println("echo examle, write 'quit' to exit");

        while(!"quit".equals(line)) {

            line = b.nextLine();

            System.out.println(line);

        }

        System.out.println("bye!");

No sé cuántas veces he tenido que escribir un InputStream entero a un OutputStream. Por ejemplo cuando necesitas copiar un fichero. Me encantaría que existiese un método writeTo() en la clase InputStream para poder hacer algo así:

new FileInputStream("foo.dat").writeTo(new FileOutputStream("bar.dat"));

Y también un método análogo para readers y writers. El método writeTo() estaría sobrecargado con una versión con dos argumetnos. Primero: la cantidad de bytes o caracteres que se desean leer y escribir (un número negativo significaría “todos”); y segundo: el tamaño de buffer que se usará. Este es un método de utilidad que uso en mis clases:

    public static void writeTo(InputStream in, OutputStream out, int buffsize, int length) throws IOException {

        byte[] buff = new byte[buffsize];

        int read = -1;

        do {

            read = in.read(buff, 0, length < 0 || buff.length < length ? buff.length : length);

            if(read == -1) break;

            out.write(buff, 0, read);

            length -= read;

        } while(true);

    }

Hay otras muchas tareas comunes a la hora de usar archivos que se tienen que implementar por uno mismo:

  • Copiar archivos.
  • Eliminar un directorio recursivamente.
  • Leer un archivo de texto a un String. Esto podría ser peligroso si el archivo es demasiado largo, así que el método podría tener un argumento para especificar el límite máximo de caracteres a leer.
  • Leer un InputStream o un Reader a un String.
  • Iterar las líneas de un fichero de texto más fácilmente. Por ejemplo en Python puedes hacer lo siguiente:
    for line in file("FileName.txt"):
    
      # Process line

Desde hace años uso CommonsIO para todas esas tareas, pero me gustaría tenerlas en el API estándar.
Integración de Swing con el escritorio

  • Quiero poder abrir un fichero con la aplicación instalada por defecto. Por ejemplo quiero que el usuario abra la ayuda de la aplicación en formato PDF con el lector de PDFs por defecto.
  • Quiero abrir una URL en el navegador por defecto. Por ejemplo quiero enviar al usuario a la página de pago en una aplicación shareware.
  • Quiero abrir el programa de correo por defecto Por ejemplo, quiero que el usuario pueda mandarme un email fácilmente desde la aplicación.

¡Por fín! Tenemos un API para eso en Java6.

Componentes Swing

  • ¡Quiero un componente para elegir una fecha en un calendario! ¡Esto es algo esencial en cualquier toolkit gráfico!
  • Por qué es tan complicado mostrar u ocultar todos los nodos en un JTree? Bien, puede ser peligroso dependiendo del TableModel porque los nodos en un TableModel se muestran bajo demanda. Un TableModel podría tener demasiados nodos (ej: todos los ficheros de un sistema de ficheros) o incluso podría tener infinitos nodos. Quizá sería útil tener un método en la interfaz TableModel: boolean canExpandAll(); y dos métodos en la clase JTree: expandAll() y collapseAll().

Algoritmos comunes

  • ¿Dónde está el soporte para codificar y decodificar en base64? Ah, lo encontré: import sun.misc.BASE64Encoder…. ¡Espera! ¡esto no es el API estándar! Si estás interesado en una implementación independiente del JRE usa este codificador / decodificador de base64 que es de dominio público.
  • Me gustaría poder hacer esto: Md5.hash(”foo”); o esto: SHA1.hash(”bar”); ¿Por qué no? ¡Y lo quiero en el API estándar!

Aplicaciones enterprise

  • Escribir XML. ¿Cuál es la forma estándar? Quiero un Document.write(…) o un Document.toString(). Vale, sé que esto depende del API W3C DOM y que existe un módulo “Load and Save” en DOM, pero no está incluído en el API estándar de Java. No me gusta W3C DOM por esta y otras razones. Si necesito una API estilo DOM prefiero JDOM.
  • Subir ficheros en una aplicación web. ¿Por qué el API de servlets es de tan bajo nivel? ¡Tengo que usar Commons FileUpload para poder leer un campo file de un formulario HTML!
  • Enviar correos electrónicos. Me encanta la función mail() de PHP. Me gustaría tener algo más sencillo que JavaMail en Java.
  • Para JDBC me gustaría tener métodos de utilidad como:
    connection.executeStatement("insert into users (first_name, last_name) values(?, ?)", "foo", "bar");
    
    connection.queryInt("select count(*) from users");

    Bueno, puedes hacer este tipo de cosas con Spring Jdbc DAO support y sus plantillas JDBC.

Propuestas de cambios de sintaxis

Además de los cambios en el API también voy a proponer algunos cambios en la sintaxis. Por ejemplo me gusta mucho la sintaxis para crear arrays en PHP. Por qué no algo así en Java:

Map map = new HashMap("first_name" => "foo", "last_name" => "bar");

O así:

List list = new ArrayList("foo", "bar");

Bueno, lo último sería posible si ArrayList tuviese un constructor con varargs.

Otra cosa que me gusta mucho es el method chaining (encadenamiento de métodos). Por ejemplo me gustaría poder hacer algo así:

        JFrame frame = new JFrame()

            .setTitle("title")

            .setIconImage(image)

            .pack()

            .setLocationRelativeTo(null)

            .setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)

            .setVisible(true);

En vez de devolver void devuelve this y podrás hacer encadenamiento de métodos. Esto no es un cambio en la sintaxis, es más bien una convención. Pero… el lenguaje podría devolver por defecto this en los métodos void no estáticos. Entonces podrías hacer encadenamiento de métodos sin implementarlo directamente.

Conclusiones

Me gusta el espíritu de Perl: “haz lo fácil, fácil, y lo difícil posible“. Y creo que en Java se podría hacer un mejor trabajo. Sin considerar los cambios en la sintaxis, ¿cuántas líneas de código se necesitarían para implementar los cambios en el API que he propuesto? Creo que no muchos. No quiero incluir todas las APIs de Apache Commons en la librería estándar. Ya hay demasiadas cosas. Pero creo que estas funcionalidades son muy útiles y fáciles de implementar. Demasiadas veces he tenido que echar un vistazo al Java Developers Almanac para encontrar la mejor forma o la más sencilla de implementar una tarea muy común. No obstante tengo que decir que Java6 ha introducido importantes cambios en el API. Y eso son buenas noticias.

Y sobre los cambios en la sintaxis se ha hecho un gran trabajo en Java 5: varargs, foreach, generics y el outboxing e inboxing. Estos cambios han hecho a Java menos “verbose“.

Resumiendo, Java es mejor en cada versión, pero podría ser aún mejor.

1 comentario »

  1. mira para evitarte el CRUD basico que es verdad es tedioso programar para quien ya se aburre de hacerlo proyecto tras proyecto, existen muchas herramientas de mapeo entidad -relacion que te pueden ahorrar el trabajo como EJB, Hibernate, ANDROMDA solo tienes que ser capaz de modelar con alguna herramienta UML y ya esta a ocuparse solo de la presentacion.. suerte con esto visita http://www.andromda.org

    Comentario por Manuel Javier JImenez Rico — Marzo 18, 2008 @ 9:22 pm

Redifusión RSS de los comentarios de la entrada. URI para TrackBack.

Deja un comentario

Blog de WordPress.com.