En octubre-96 impartí una conferencia titulada “Java y su Entorno Tecnológico” en la Universidad de Murcia y, para relativa sorpresa mía, me percaté de dos hechos singulares: el solo nombre “Java” atrae a la gente sin remedio (hubo que restringir la entrada a alumnos de quinto curso, doctorandos y profesores, y aún así muchos asistentes quedaron sin asiento) y, en general, las expectativas del público son muy diversas y se centran, sobre todo, en applets e Internet, pero no en invocación de métodos remotos, serialización de objetos, arquitecturas de componentes o esquemas de persistencia). Y es que, tras obviar applets e Internet, Java resulta ser más que un lenguaje: más bien un cúmulo de adaptaciones, revisiones y hallazgos tecnológicos. Naturalmente esto plantea, aparte de la explicación del pasmo (que se reserva a sociólogos o psicologistas [1]), una necesidad: la de establecer una suerte de guía práctica que sucintamente explique y pondere los distintos aspectos tecnológicos que en la actualidad se agrupan bajo el pararrayos comercial de Java y que en absoluto se limitan al mero lenguaje. Bien: sin ánimo acaparador, he aquí mi modesta contribución. Un aviso: no se sorprendan si no encuentran dulzor de arrope o estúpidos esquemas publicitarios: a Java se le supone el valor (como la náusea vital al soldado), así que tratémoslo como a un adulto [2]. PROMESAS, PROMESAS Java promete muchas cosas, pero la mayor parte de la publicidad está singularmente orientada-a-objetos: es decir, al tipo de lector/oyente/vidente que se maravilla con el aluvión de adjetivos. Si ponemos, empero, el cazo a hervir y eliminamos el agua que sobra, nos encontraremos, grosso modo, con tres conceptos prometidos (que alguno pensará prometeicos):
Claro que estas son las promesas de siempre, pero el hecho que Java las repita incesantemente, por delante de todo, a mí me recuerda a los pollos que, antes de solicitar la mano de una doncella, ponderaban sus propias virtudes y planes futuros, intentando aliviar la temida pregunta “Sí, sí, pero … ¿con qué cuenta en la actualidad?”. APRECIACIONES ERRÓNEAS Cornell y Horstmann reúnen y detallan en su libro “Core Java” algunos de los más frecuentes equívocos respecto del lenguaje y su(s) entorno(s):
EL LENGUAJE JAVA ES … En su libro “Hooked on Java” Arthur Van Hoff se despacha con el siguiente rosario de características de Java, que se entienden como razones de adopción, mimo e inmensa satisfacción personal [5]: “Java es simple, orientado-a-objetos, tipado estáticamente, compilado, arquitectónicamente neutro, multienhebrado, con recolector de basura, robusto, seguro, extensible y aprehensible. ¡Y además es divertido!”. Pero, bueno, ¡Tanto lugar común es una provocación! Dispersemos al enemigo y ataquemos por separado. Yo, como siempre, oficiaré de abogado del diablo y segregaré jugos de distinta consideración política. JAVA ES SIMPLE Resulta que Java ha suprimido las características (supuestamente) “indeseables” de C++: ficheros de cabecera, aritmética de punteros, sobrecarga de operadores, estructuras, uniones, conversiones implícitas de tipos, clases base virtuales, preprocesador, herencia múltiple de implementación, etc. Naturalmente la cuestión es que la “simpleza” de Java se convierte en simpleza-respecto-de … ¡C++! Es cierto, empero, que en la mayoría de proyectos reales C++ una de las primeras tareas es delimitar el subconjunto del lenguaje que podrá ser usado. El problema con Java es que la poda de C++ no ha sido consistente y algunas apresuradas decisiones de diseño [6] han fragmentado la buscada cohesividad del lenguaje. JAVA ES ORIENTADO-A-OBJETOS Pues sí: en Java casi todo son objetos. O sea: están los objetos por un lado y los tipos predefinidos por otro. Ah, y las clases en aquella esquina, dispuestas todas ellas en herencia jerárquica cósmica: todas descienden de la clase “Object”. Porque finalmente Java es tan híbrido u orientado-a-objetos como C++. La mayor distinción es que en el diseño de C++ se hizo mucho hincapié en que tipos predefinidos y tipos definidos-por-el-usuario (clases) fueran ciudadanos de la misma categoría (y por esto la sobrecarga de operadores, las conversiones implícitas, etc.). En Java el desprevenido programador se encuentra, sin embargo, con que un Vector de Objetos no puede contener … ¡floats! Claro que Java ha intentado solucionar este punto mediante clases que actúan de envoltorios (wrappers) inmodificables de los tipos predefinidos: Integer, etc. Java provee también herencia simple -como la mayoría de los lenguajes OO-, clases abstractas, e interfaces (protocolos) similares a interfaces IDL CORBA. En resumen: Java provee herencia y resolución de operadores en tiempo de ejecución: es, por tanto, un lenguaje orientado-a-objetos. Punto. JAVA ES TIPADO ESTÁTICAMENTE Esta característica, evidentemente heredada de C++, felizmente origina que la mayoría de los errores se detecten en la compilación. Pero Java proporciona, además, una información similar a la de C++ sobre tipos en ejecución, además de incorporar chequeos de determinados objetos en ejecución (rangos de arrays, etc.). Precisamente este chequeo de tipos origina que Java sea un lenguaje dicotómico (clases-objetos) y trae consigo las ventajas y desventajas de este enfoque ya extensamente comentadas respecto de C++ y en contraposición a, verbigracia, Smalltalk. Por supuesto que Java no es un mejor Smalltalk (De hecho el modelo de metaclases en Java es particularmente débil), al que habría añadido el chequeo de tipos; como tampoco es un mejor C++. La supuesta desventaja del control de tipos es la pérdida de la fuerza expresiva y flexibilidad propia de lenguajes como Smalltalk y Self. Pero ésta es también una vieja discusión a la que Java no aporta nada nuevo. JAVA ES COMPILADO ¡Demonios! ¿No era interpretado? Bueno, es cuestión de secuenciación. La compilación de los programas Java (aquéllos con la extensión “.java”) genera “ficheros de clase” (ya saben: con la extensión “.class”) que contienen bytecodes: esto es, un código intermedio que es interpretado por la máquina virtual Java, que precisamente es la encargada de resolver dinámicamente las referencias a métodos e instancias contenidas en los ficheros de clase. De cualquier manera Java, como el resto de los lenguajes, dispone (y dispondrá) de compiladores nativos para los distintos sistemas en que se implemente la plataforma Java (note el lector que nos referimos aquí a compiladores que generan código nativo a partir de los ficheros de clase ya compilados). Existen inconsistencias en el modelo orientado-a-objetos de Java que originan distintos transtornos en el intérprete, como la ejecución de métodos en objetos que aún no han sido creados, pero es de suponer que tales “bugs” serán solucionados en la próxima versión. Por supuesto no les aliento a “crackear [7]” la máquina virtual Java (JVM: Java Virtual Machine) como propone Chris Laffra en un capítulo de su libro “Advanced Java” (no tan avanzado, con todo). JAVA ES ARQUITECTÓNICAMENTE NEUTRO Como vimos en el párrafo anterior el compilador Java genera bytecodes que serán ejecutables dondequiera que exista una máquina virtual Java. Esta es la promesa, fácil de cumplir si uno se sujeta a parecidas restricciones impuestas a príncipes y princesas en los cuentos de Perrault. Java expresamente promete: “Write once, run anywhere, anytime, forever …”, pero, claro, esto dependerá exactamente de si en nuestro código Java hacemos uso sólo de las clases estándar del lenguaje o no. Lo cierto es que la idea del código intermedio no es nueva (UCSD Pascal ya usó de ella), y durante tiempo se ha discutido si el bytecode Smalltalk representaba o no una auténtica compilación. Ya ven: discusión resuelta. Lo cierto es que la literatura Java firma pagarés que quizás sus bibliotecas no puedan pagar. JAVA ES TRANSPORTABLE Java utiliza caracteres Unicode, lo que asegura su uso en distintos lenguajes (y, por ejemplo, el uso de caracteres acentuados o eñes en los identificadores). Por otro lado el tamaño y comportamiento aritmético de los tipos predefinidos es único para todas las plataformas, y no como, por ejemplo, C++ que, por razones de eficiencia, deja tales cuitas de implementación a cada compilador. Naturalmente con Java se acabaron los desbordamientos de pila ocasionados por la migración de código de uno a otro sistema. Hay que resaltar que, así como distintas estructuras de datos (Vector, List, etc.), las bibliotecas gráficas son parte del sistema. De hecho la biblioteca AWT proporciona un mediocre aspecto en todas las plataformas soportadas [8], y ya sabe el lector que la mediocridad es buena base para la transportabilidad. Cabe repetir aquí que la AWT es una biblioteca con serias deficiencias de diseño, implementación y homogeneidad conceptual. JAVA ES LIMPIO El recolector de basura/despojos (GC: garbage collector) se ocupa de liberar automáticamente los recursos asignados por el programador, y como además el GC se ejecuta en su propia hebra (thread), no resulta intrusivo respecto de las otras tareas del aplicativo [9]. Pero no todo es automático: algunos recursos no pueden (deben) liberarse automáticamente y dependen de la arquitectura/implementación de las bibliotecas utilizadas. Los mecanismos de intervención manual en la desasignación de recursos son, por otro lado, confusos y no resisten el embate de programadores aviesos: es relativamente sencillo crear objetos zombis que no pueden ser “recolectados” por el GC (el lector malvado podrá encontrar más información sobre objetos zombis en “Advanced Java” de Chris Laffra y en “The Java Language Specification” de Gosling, Joy y Steele. JAVA ES MULTIENHEBRADO En la mayoría de lenguajes de programación tiene que usarse un paquete externo para operar con multi-threading, mientras que Java posee esta característica embebida en el lenguaje mismo (digamos en la plataforma misma). Lo malo es que Java no contempla una implementación independiente del multi-threading, de forma que, aunque el código de llamada sea siempre el mismo, la implementación se traspasa (matizadamente) a los sistemas operativos en cada caso subyacentes. La cuestión es que también dependemos de las decisiones de diseño que el JDK toma respecto de cada plataforma: así por ejemplo el JDK 1.0x usa en Solaris “green threads”, por lo que no se aprovecha adecuadamente el time slicing de las hebras nativas de Solaris. JAVA ES ROBUSTO Se dice que el modelo de punteros de Java elimina la posibilidad de sobre-escribir memoria o corromper datos. Pero … ¿Java tiene punteros? ¡Pues sí! Lo que ocurre es que Java no deja al programador operar con ellos (esto es, suprime la denominada “aritmética de punteros” en aras de la seguridad del código [10]). Tal modelo de punteros se basa en la siguiente dicotomía:
De esta forma se entiende que, aun siendo siempre en Java el paso de parámetros “por valor”, se mantenga la eficiencia en las llamadas a métodos. Claro que esto supone que no se puede codificar en Java una función del tipo “swap” tan habitual en C/C++ (pues los cambios locales no afectarán a las variables en el ámbito de la llamada al método), pero sí se podrá acceder a cualquier objeto desde el cuerpo de una función a la que se le pase como parámetro (con las restricciones de las etiquetas de acceso, naturalmente). Java incorpora, por otro lado, un excelente sistema de tratamiento de excepciones, similar al de C++. Claro que la captación de excepciones en C++ ha sido considerada desde el principio como un sistema de “gotos” por los puristas (lisez Eiffelistas o meyerianos). JAVA ES PEQUEÑO Uno de los objetivos de Java es que pueda ejecutarse en máquinas de poca capacidad: el soporte de clases y el intérprete básico ocupan 40kb, mientras que las bibliotecas estándar y el multi-enhebramiento ocupan 175kb más. Claro que conforme se añada funcionalidad (algo necesario, por otra parte), la plataforma crecerá sin remedio. Y es que la cuestión es: ¿cómo podrá segmentarse la plataforma Java (o el sistema operativo Java) para acoplarse tanto a pequeños electrodomésticos como a terminales windows-like? Claro que quizás los “Java beans” sean buena solución, pero de esto hablaremos en la próxima entrega. JAVA ES RÁPIDO La especificación de bytecodes de Java es particularmente eficiente, pero no hay que olvidar que es mero combustible para el intérprete, de forma que la literatura asocia al código Java así ejecutado una rapidez 20 veces menor que la del código C equivalente. Claro que la plataforma Java incorporará (y lo hace) compiladores JIT (Just-In-Time), cada vez más eficientes, que generarán código nativo para cada plataforma específica. Es claro, también, que Java no toma de su propia medicina: la mayoría de sus herramientas y entornos están codificadas en C++, y no es mera casualidad que el entorno de desarrollo “Java Workshop” de Sun, enteramente codificado en Java, sea el más lento de todos (pese a que Sun ha prometido una impresionante mejora de prestaciones en la próxima versión de su herramienta). Con todo, hay que notar aquí que Java es suficientemente rápido en la actualidad para cubrir con honra (y evitaré aquí la broma cruel) la mayoría de aplicaciones de escritorio actuales. La eficiencia de las estructuras de datos en Java, por otro lado, deja mucho que desear y no tiene comparación alguna, práctica o semántica, con la STL de C++. Con todo vale la pena reseñar que Matthew Austern y Alexander Stepanov están trabajando en una colección de algoritmos genéricos en Java (JAL: Java Algorithm Library [11]) cuya principal diferencia respecto de la biblioteca STL de C++ es que, asumiendo que el lenguaje Java carece de genericidad (err … ¿no lo comenté antes?), JAL opera en arrays unidimensionales y utiliza scripts Perl para instanciar y crear los “packages” Java apropiados para cada tipo deseado. JAVA ES SEGURO El hecho que los bytecodes puedan verificarse antes de su ejecución facilita enormemente el control de los aplicativos. Y es que resulta que los programas Java son fácilmente desemsamblables. Pero no teman: el resultado comercial será muy parecido al que se originó con la entrega del código fuente de las bibliotecas en C++, que hoy a nadie turba ni sorprende. Un programa Java no puede, por otro lado, corromper memoria fuera de su espacio de proceso ni acceder a ficheros locales si el contenedor del programa (verbigracia, un visor web) es prudentemente “juicioso”. Pero, ¿seguridad, seguridad? ¡Humm, esperen a los interfaces de seguridad del JDK 1.1! JAVA ES EXTENSIBLE Java puede utilizar bibliotecas software codificadas en otros lenguajes y, de hecho, tal real posibilidad se contempla en el mismo lenguaje, de forma que en el interior de un programa Java pueden declararse métodos “nativos” cuyo código externo se cargaría dinámicamente en la máquina virtual Java. Claro que la integración de lenguajes no es asunto trivial y, además, el multienhebramiento embebido en Java troca más difícil la integración (pues la mayoría de las bibliotecas de C/C++ no son multienhebradas). El lector interesado en la correspondencia (mapping) de Java con lenguajes como C/C++ puede echarle un vistazo al apéndice dedicado a métodos nativos en “The Java Programming Language”, de Arnold y Gosling. JAVA ES APREHENSIBLE Según sus autores Java reúne lo mejor de los distintos lenguajes (Smalltalk, Objective C, C++, Self, Beta, Pascal, Lisp y Cedar) y obvia lo peor de los mismos. Parece así que Java es una “buena” mezcla: los conceptos no son nuevos y su adecuación inteligente. Y de aquí equivocadamente se infiere que la comprensión está garantizada [12]. El problema es que la técnica “Nueve ginecólogas de cada diez aconsejan y usan …” no tiene por qué funcionar. Y es que el “picking” requiere de un cuidadoso estudio, de un sentado diseño y de una implementación bien probada: circunstancias todas que no se han dado en Java, de forma que quizás habría que esperar a Java++. Pero no: lo cierto es que, mención aparte del lema comercial, Java no es más incomprensible que otros lenguajes y probablemente lo sea menos. JAVA ES DIVERTIDO (?!) ¿Se supone que Java es divertido porque dota de contenido animado a las páginas web o porque uno se divierte con él? Java es un lenguaje que requiere un gran esfuerzo manual, y esto puede ser meritorio, pero no divertido. ¿O es que acaso Duke® (la mascota Java) es divertido? Porque, si Mickey es un ratón y Pluto un perro, ¿qué son Duke y Goofy? Y, por otro lado, ¿vieron como se mueve Duke? ¿Tendrá Sun (o el enmascaramiento societario que en ese momento impere) que crear una mascota compañera de otro sexo para evitar acusaciones de homosexualidad o de incorrectitud política [13]? ¿C++ y Smalltalk son también divertidos o es que Java es, simplemente, “travestido”? Oh, decididamente no sé si Java es divertido, pero su entorno (en el más risueño sentido del término) sí lo es, ¡voto a bríos! DESPROPÓSITOS JAVA En su interesante libro “Thinking in Java” (todavía no publicado), Bruce Eckel plantea lo que él considera errores mendaces y despropósitos, usualmente admitidos como lugares comunes, en el universo Java. Hay que reseñar que Eckel es, ante todo, un experto en C++, por lo que las comparaciones son más que casualidades referenciales. De cualquier manera, y dado que algunos aspectos ya fueron comentados anteriormente, expondré únicamente los titulares de lo que son (y yo coincido casi totalmente) barbaridades y mentiras:
EL CÓNYUGE PERFECTO Ciertamente Java “podría” haber sido un gran lenguaje de programación de propósito general, pero los imperativos de mercado y su demasiado pronta liberación en la comunidad de programadores han supuesto la observancia futura de cuitas de compatibilidad mayormente nocivas para el lenguaje mismo y sus bibliotecas. Claro que compañías muy conocidas obvian misteriosa y comercialmente (aquí se esfumó lo misterioso) lo que se denomina “compatibilidad-hacia-adelante”. CONCLUSIONES PARCIALES He de confesarles que me gusta el lenguaje. Quizás sea porque le haya dedicado ya mucho tiempo y uno se resista a pensar que pueda ocuparse con dedicación de cosas banales. Quizás sea por las bondades evidentes del lenguaje o por otras un tanto escondidas. Quizás sea, al fin, por la tecnología que lo envuelve. De cualquier forma resulta claro que el lenguaje Java es el punto de partida y condición sine qua non para acceder a las tecnologías que pululan tras él (acceso con drivers de diversos tipos a bases de datos/objetos, esquemas de distribución de software, integración CORBA, etc.), siendo así que yo calificaría su estudio de imprescindible. PRÓXIMA ENTREGA En el siguiente artículo revisaré los applets desde un punto de vista no-Internet y abordaré dos tecnologías Java de integración de componentes en supuesta disputa: ActiveX y Java Beans, junto con las oportunidades y riesgos que su adopción conllevaría para las empresas de software; además examinaré aspectos de JDBC (acceso a bases de datos), interfaces IDL, RMI (invocación de métodos remotos), nuevas arquitecturas de eventos, … ¡uf! ¡Demasiadas cosas, aun a título de sucinta guía! Además, en un mes-Java pueden aparecer tantas cuitas nuevas …
[1]“La tendencia humana a juzgar importantes las pequeñas cosas ha producido muchas cosas grandes”, que decía Lichtenberg, perfectamente puede aplicarse a Java. [2] Esta nota viene a cuento de los comentarios de algunos de los asistentes a mis conferencias, que de la crítica infieren mi postura anti-Java. ¡Muy al contrario! Claro que para saber de mis opiniones y muy subjetivas conclusiones el lector habrá de esperar al final del artículo. [3] Claro que con el JDK 1.1 todo cambiará … según Sun. [4] Me imagino al creador del lenguaje Java en un congreso/casino presentándose: “Gosling” … “James … Gosling” … “y sírvame unos beans agitados, no batidos”. [5] Satisfacción ante todo para el mismo van Hoff, receptor junto con sus socios Payne, Polese, etc. de una dotación de $100.000.000 de capital-riesgo (Java Funds) para su compañía Marímba, cuyos primeros resultados pueden verse ahora en Internet para sorpresa, pasmo y bochorno de los internistas. [6] Java ha sido diseñado (mejor rematado) con tanto apresuramiento que incluso existen errores ortográficos en alguna clase estándar (como “ Cloneable ”, cuyo nombre es de esperar sea felizmente restituido a “ Clonable ”). [7] Humm … ¡no pude resistirme al barbarismo! Pero, vamos, seguro que el lector es partícipe/cómplice de esta adecuada transgresión con matización onomatopéyica. [8] Respecto al transporte o “traducción” multiplataforma del código cabe aplicar lo que pensaba Carl Bertrand de las mujeres: “si son fieles no son bellas, y si son bellas no son fieles”. [9] El lector que haya trabajado con Smalltalk ya sabe que cuando entra en acción el GC la aplicación se colapsa: y tal entrada usualmente se produce cuando se está ejecutando una demostración, se está enseñando al responsable del departamento o sencillamente cuando es crucial que no se den esperas de ningún tipo. Así es la informática, piensa el sufrido Smalltalker. Claro que en Java el GC en su propia hebra cambia este asunto. [10] Que “La seguridad es la inteligencia de los tontos” es en lo que la Vizcondesa de Vanssey y los programadores avezados de C/C++ están de acuerdo. Y es que ¿vieron alguna vez a un programador experimentado de C++ que realmente tuviera que lidiar con las inseguridades que se anuncian aquí? Claro que, ¿cómo demonios identificamos a tal experto programador? [11] En “http://reality.sgi.com/employees/austern/java/index.html” hallarán los lectores javahólicos la medicina adecuada. O un nuevo tormento. Como razona un amigo, uno empieza con ilusión pero pronto se encuentra … ¡aJavado! [12] “En un séptimo de segundo se puede mandar un telegrama alrededor del mundo, pero transcurren años para que una sencilla idea atraviese seis centímetros de cráneo y se introduzca en el cerebro de un hombre” (Ketterling) [13] El lector inquieto puede preguntarse por “Daisy”, “Minnie” y la vocecita atiplada de “Mickey”. De nuevo el único que se libró fue “Goofy” |
||
| Pº. Castellana 188, 14º e · 28046 - Madrid · info@a4devis.com |
||