Soy nuevo en Kotlin. Estoy siguiendo un tutorial donde la parte de la GUI involucra este fragmento de código:
sampleList.addMouseListener(object: MouseAdapter() { override fun mouseClicked(mouseEvent: MouseEvent?) { if (mouseEvent?.clickCount == 2) { launchSelectedSample() } } }) mouseEvent es obviamente algo anulable . Estoy acostumbrado, en la experiencia de codificación anterior, a cambiar una línea como mouseEvent?.clickCount == 2 a mouseEvent?.clickCount > 1 (o quizás >=2 ), para asegurarme de que no haya un caso de esquina donde los clics sucedan tan rápido que salta del 1 al 3, o algo similar.
Entonces, cambié este código a:
sampleList.addMouseListener(object: MouseAdapter() { override fun mouseClicked(mouseEvent: MouseEvent?) { if (mouseEvent?.clickCount >= 2) { launchSelectedSample() } } }) Al hacer el cambio ( cambiando ==2 a >=2 ), recibí el siguiente error de IntelliJ:
Operator call corresponds to a dot-qualified call 'mouseEvent?.clickCount.compareTo(2)' which is not allowed on a nullable receiver 'mouseEvent?.clickCount'.Esto me planteó 2 preguntas:
==2 funciona bien, pero >=2 no funciona? (Probé >1 , que dio el mismo error que >=2 ). Me gusta la idea de garantizar que los valores null no arruinen las cosas durante el tiempo de ejecución, pero desearía que Kotlin simplemente se deshiciera de los valores null e hiciera algo como Rust o Haskell. (Sin embargo, me gusta lo que he visto de Kotlin hasta ahora).
Como ha descubierto, los operadores de igualdad de Kotlin ( == y != ) pueden manejar valores nulos, mientras que los operadores de comparación de órdenes ( < , <= , > , >= ) no pueden.
Esto probablemente se deba a que es obvio lo que deberían significar las comprobaciones de igualdad para los valores nulos: dos valores nulos son claramente iguales y un valor que no es nulo nunca debe ser igual a un valor nulo, mientras que no está del todo claro qué debería significar para las comparaciones de pedidos. (Si nulo no es < 0, ¿significa eso que nulo >= 0? Si no es así, ya no tiene un orden bien definido).
Esto se refleja en la implementación: Any tiene un método equals() , lo que indica que se puede verificar la igualdad de todos los objetos. (La documentación de Kotlin lo hace explícito, al igual que para el método subyacente de Java , que los objetos no nulos nunca deben ser iguales a nulos). Y la implementación de Kotlin de los operadores == y != verifica explícitamente los nulos. ( a == b se traduce a lo que tienes que deletrear en Java: a == null ? b == null : a.equals(b) .
Pero la comparación de pedidos se maneja de manera diferente. Utiliza la interfaz Comparable : solo los tipos con un 'ordenamiento natural' implementan eso; los que no, no se pueden comparar de esa manera. Dado que null no puede implementar ninguna interfaz, no puede tener un orden natural y el compilador le impide intentar la comparación. (La documentación de Kotlin no hace esto explícito, porque el parámetro no admite valores NULL; pero para la interfaz Java subyacente dice que dicha comparación debería devolver una excepción NullPointerException).
En cuanto a cómo debe manejar esto, el operador Elvis es probablemente la solución más concisa:
if (mouseEvent?.clickCount ?: 0 >= 2) Si mouseEvent no es nulo, obtendrá su clickCount ; de lo contrario, la llamada segura ?. dará el nulo directamente, y luego ?: sustituirá a 0. (Eso también sucedería si clickCount nulo, aunque eso no debería ser posible). En todos los casos, terminas con un entero no anulable que puede compararse con seguridad con 2.
Por supuesto, en la práctica, nada debería llamar a un método de escucha y pasar un evento nulo. (No recuerdo haberlo permitido nunca cuando solía escribir código Java Swing para ganarme la vida, o tener algún problema como resultado). Entonces, una alternativa más simple podría ser declarar el parámetro como no anulable. Pero manejar el valor nulo correctamente es un poco más seguro; y en este caso, no agrega mucho código extra. ¡Eso depende de ti!
¿Qué comportamiento espera de mouseEvent?.clickCount >= 2 cuando mouseEvent == null ?
Podría transformar su mouseEvent?.clickCount a NotNull usando el operador elvis ( ?: :):
val clickCount = mouseEvent?.clickCount ?: 0 if (clickCount >= 2) { launchSelectedSample() } En este caso mouseEvent?.clickCount será 0 si mouseEvent es nulo
==2 funciona porque mouseEvent?.clickCount considerado por Kotlin como null y la comparación null == 2 es correcta a diferencia null >= 2
un poco mas estilo kotlin puede ser este:
mouseEvent?.clickCount?.let{ if(it >= 2) { launchSelectedSample() } }estilo aún más funcional:
mouseEvent?.clickCount?.takeIf { it >= 2 }?.let { launchSelectedSample() }