No tengas miedo de usar var en Java

Advertisements

Introducida como una nueva función en Java 11, la inferencia de tipo en variables locales permite usar la palabra var en lugar del tipo de dato al declarar una variable en códigos de bloque locales, y el compilador puede inferir el tipo de dato correspondiente de esa variable a partir del valor estás asignando. Esa es una característica que se puede encontrar en otros lenguajes JVM (Scala, Kotlin).

Una de las características de Java como lenguaje es que es totalmente tipado, y con esta función continúa como lenguaje seguro para tipos. La función usa la información y el contexto que el compilador ya tienen para determinar ese tipo de dato al asignar la variable, una vez que se asigna, el tipo no cambiará (a diferencia de lenguajes no tipados, como en JavaScript o Python).

El uso inteligente de esta función puede ayudarnos a hacer que nuestro código sea más legible y menos “verboso” (una queja común del código Java). Sin embargo, hay desarrolladores que creen que la función es un anti-patrón, un “code smell” y debe evitarse. Es por eso que he decidido compartir algunos puntos sobre por qué var es muy útil en algunas situaciones y no debes tener miedo de usarlo.

Porqué algunos programadores podrían querer evitarlo?

Analicemos algunas de las razones por las que algunos desarrolladores podrían querer evitar usar esta función.

1) Oculta el tipo y requiere esfuerzo para descifrarlo

One of the reason why some developers say we should not use the var “keyword” in Java is because “using var is hiding the type from the person reading the code and will require them to have to figure out what is being done“.

Una de las razones por las que algunos desarrolladores dicen que no deberíamos usar el “keyword” var en Java es porque “usar var oculta el tipo de la persona que lee el código y requerirá que tenga que averiguar qué se está haciendo”.

public void myMethod(){
  var counter = 0;
  var message = "";
  while(counter < 10){
    //some logic here
    message += "some value";
    count++;
  }
}

Pero veamos ese escenario. No necesita averiguar cuál es el tipo, está solo unas pocas líneas arriba. Se requiere el mismo esfuerzo para verificar el tipo para ver el lado derecho de esa declaración y darse cuenta de que es un número o una String. Y ese esfuerzo es mínimo, la declaración está en el mismo bloque de código unas líneas más arriba.

Y si necesita desplazarse mucho hacia arriba para ver la declaración de la variable para ver el tipo, el problema podría no ser el tipo inferido y no explícito, el gran problema es que el nombre no es significativo y, también podría necesitar refactorizar su método para reducir las líneas de código que requiere. Esos dos son el “code smell” más importantes para manejar.

2) Puede ser confuso con polimorfismo.

Otra razón, por el polimorfismo, podría ser confuso determinar cuál es el tipo. Un ejemplo que he visto para “apoyar” esa idea.

public class MyService {
 	public Mustang getCar(){}
}
public class Mustang extends Car {}

//Another place in the code 
void myMethod(){
	var myCar = service.getCar();//Aquí el tipo de dato no es explicito
    ....
    //muchas lineas después
    //Que tipo de dato podría ser myCar?
}

Aquí, nuevamente, el problema es no usar nombres significativos para el método y la variable. Si el método getCar() devuelve un Mustang, tal vez nombrar el método getMustangCar() sea una mejor opción. Además, nombrar la variable var myMustangCar en lugar de solo myCar es una mejor práctica.

En realidad, en el escenario anterior no estamos usando polimorfismo, porque está devolviendo un tipo “concreto”, no la abstracción. Y usar la palabra clave var nos obliga a seguir otras buenas prácticas cuando puedo usar mejores nombres que mejoran la legibilidad del código.

3) No sé el tipo de retorno del método.

Estoy parcialmente de acuerdo con este argumento, podría haber algunos escenarios en los que tal vez solo por el nombre del método y el nombre de la variable durante la declaración no haya suficiente información o contexto para determinar el tipo.

var descriptor = service.getDescriptorSpecification();   
var ratio = foo.calculateRatio();

Eso es cierto, ¿Cuál podría ser el tipo de dato para descriptor? podría ser un String, Map u otra Clase, no lo sabemos. Si queremos usar algún comportamiento de descriptor, podría ser más difícil darse cuenta del tipo de dato.

Para esos casos, usar el tipo de dato explícitamente podría ser la mejor opción.

Map<String, String> descriptor = service.getDescriptorSpecification();
int ration = foo.calculateRation();

Sin embargo, hoy en día, el 99% de los desarrolladores de Java usan IDE (IntelliJ, VS Code, NetBeans, Eclipse, etc.) con la capacidad de proporcionarnos el tipo. El IDE generalmente agrega una etiqueta o información sobre herramientas junto a la declaración de la variable, o muestra el tipo al mover el mouse sobre el método, y cuando usamos la variable, el IDE nos brinda la opción de auto-completar que muestra las opciones para ese tipo.

Los desarrolladores de Scala y Kotlin no se quejan porque el tipo no siempre está ahí, Por qué deberíamos hacerlo en Java?.

Advertisements

¿Has olvidado las variables de entrada de expresiones lambda?

¿Qué pasa con las variables de entrada de las expresiones lambda? Algo como estos:

//Example 1
(a, b) -> a+b
//Example 2 
v -> v.toUpperCase().trim()
//Example 3 
d -> d.doSomething()
//Example 4  
(x, y) -> { //What type is x, what type is y?
  String message = "";
  if(x > 20){
    message = "dummy";
  } else {
    message = y.getMessage().trim();
  }
  return message;
}

En este caso, el compilador infiere el tipo de dato y no nos proporciona contexto – cómo el lado derecho de la asignación -, para determinar su tipo. Además, es muy común usar solo nombres cortos en las variables (no significativos). Se debe entonces confiar en las capacidades del IDE o saltar a la definición de la clase para ver la interfaz funcional y los tipos de parámetros de sus variables de entrada.

Sin embargo, los desarrolladores no se quejan de esa función… Interesante… Si usted, como desarrollador, usa la expresión lambda sin quejarse del tipo que se infiere, ¿por qué se quejaría de la palabra clave var en otros bloques locales?

Buenos casos de uso para var

Entonces, como se mencionó anteriormente, podría haber algún escenario en el que haya algunos argumentos sobre si usar var o no podría ser útil o no. Sin embargo, hay algunos escenarios en el que usar var es la mejor opción y nada puede argumentar lo contrario.

Haciendo Genéricos más legibles readable

Introducido en Java 5, Generics permite al desarrollador crear patrones de seguridad de tipos más potentes, pero puede hacer que su código sea muy “verboso”.

Map<String,List<MyClass>> myMap = new HashMap<String, List<MyClass>>();

Hay algunas mejoras para reducir la verbosidad y también inferir el tipo en Java 7. Sin embargo, var es el pináculo de esas mejoras.

var myMap = new HashMap<String, List<MyClass>>();

Realmente reduce la verbosidad de nuestro código y lo hace más legible.

Clases con nombres largos

Una buena práctica es usar nombres de clase significativos, pero en algunos casos puede hacer que se creen clases con nombres muy largos.

class SnafuVeryLongFooBarDescriptorMap {}
//In another place in the code 
public void myMethod(){
  SnafuVeryLongFooBarDescriptorMap myMap = new SnafuVeryLongFooBarDescriptorMap();
}

En ese caso, podría considerarse redundante e innecesario definir dos veces el nombre largo de la clase. Y crean en mí, he visto nombres más largos que ese.

class SnafuVeryLongFooBarDescriptorMap {}
//In another place in the code 
public void myMethod(){
  var myMap = new SnafuVeryLongFooBarDescriptorMap();
}

Inferencia de tipo en variable Local – var– al rescate !!!

Conclusión

No estoy diciendo que todos deberían usar var en todas partes, simplemente NO TENGAN MIEDO DE USARLO, si hay un escenario en el que usar var mejora la legibilidad o reduce la verbosidad, usémoslo, se agregó al lenguaje con un propósito.

Java se considera un lenguaje verboso (en comparación con otros lenguajes), la mayoría de las veces se debe a Type Safe proporcionado por el lenguaje, hay una buena función disponible para reducir la verbosidad en algunos casos, como los genéricos, y manteniendo la seguridad de tipos de datos.

Con 4 años creando también código en Scala, y creando algo de código en Kotlin, no he visto a desarrolladores quejarse porque el tipo es inferido y usan var o val , lo usan en muchos casos, y usan tipo explícito en otros casos, simplemente siguiendo buenas prácticas en la convención de nomenclatura y las capacidades de los IDE modernos.

Al hacer revisiones de código (tal vez desde Git Hub) donde no estoy usando un IDE moderno, tener una buena nomenclatura en variables y métodos ayuda más que el tipo de dato explícito de la variable. Aprovecha el var cuando sea posible, e incluso te obliga a implementar otras buenas prácticas.

NO TENGA MIEDO DE UTILIZAR VAR, utilícelo cuando ayude con la legibilidad y utilícelo sabiamente.

Referencias

Advertisements

Leave a Reply

Your email address will not be published. Required fields are marked *