Mejores Prácticas
## Objetivos de Aprendizaje
- Aprender convenciones de código en Kotlin
- Comprender Kotlin idiomático
- Trabajar con interoperabilidad Java
- Aplicar mejores prácticas en desarrollo Android
## Convenciones de Código
### Nomenclatura
```kotlin
// Classes: PascalCase
class UserAccount
// Functions: camelCase
fun calculateTotal()
// Properties: camelCase
val userName = "Alice"
// Constants: SCREAMING_SNAKE_CASE
const val MAX_RETRY_COUNT = 3
// Packages: lowercase
package com.example.myapp
```
### Indentación y Formato
```kotlin
// 4 spaces for indent
fun main() {
if (condition) {
doSomething()
}
}
// Chained calls
val result = items
.filter { it > 0 }
.map { it * 2 }
.firstOrNull()
```
### Documentación
```kotlin
/**
* Calculates the sum of two integers.
*
* @param a First integer
* @param b Second integer
* @return The sum of a and b
*/
fun sum(a: Int, b: Int): Int = a + b
```
## Kotlin Idiomático
### Tipos Anulables
```kotlin
// Bad
if (name != null) {
println(name.length)
} else {
println(0)
}
// Good
println(name?.length ?: 0)
```
### Plantillas de Cadenas
```kotlin
// Bad
println("User: " + user.name + ", Age: " + user.age)
// Good
println("User: ${user.name}, Age: ${user.age}")
```
### Valores por Defecto
```kotlin
// Bad
fun createUser(
name: String,
age: Int,
email: String,
phone: String
) {
// ...
}
createUser("Alice", 30, "alice@example.com", "")
// Good
fun createUser(
name: String,
age: Int = 18,
email: String = "unknown@example.com",
phone: String = ""
) {
// ...
}
createUser("Alice")
```
### Data Classes
```kotlin
// Bad
class User {
val name: String
val email: String
constructor(name: String, email: String) {
this.name = name
this.email = email
}
fun equals(other: User) = name == other.name && email == other.email
fun hashCode() = name.hashCode() * 31 + email.hashCode()
fun toString() = "User(name=$name, email=$email)"
}
// Good
data class User(val name: String, val email: String)
```
### Operaciones de Colecciones
```kotlin
// Bad
val result = mutableListOf()
for (item in items) {
if (item.isNotEmpty()) {
result.add(item.uppercase())
}
}
// Good
val result = items
.filter { it.isNotEmpty() }
.map { it.uppercase() }
```
### apply y with
```kotlin
// Bad
val person = Person()
person.name = "Alice"
person.age = 30
person.email = "alice@example.com"
// Good
val person = Person().apply {
name = "Alice"
age = 30
email = "alice@example.com"
}
// with for transforming
val info = with(person) {
"$name is $age years old"
}
```
## Interoperabilidad Java
### Llamando Java desde Kotlin
```kotlin
// Java method returning null
val javaObject = JavaClass()
javaObject.nullableMethod?.let { /* safe call */ }
// Java method with @Nullable annotation
val result: String? = javaObject.method()
```
### Llamando Kotlin desde Java
```kotlin
// Kotlin top-level function
// Java calls: FunctionsKt.repeat("Hello", 3)
// Kotlin object
object MySingleton {
fun doSomething() {}
}
// Java calls: MySingleton.doSomething()
// Kotlin companion object
class MyClass {
companion object {
fun create() = MyClass()
}
}
// Java calls: MyClass.create()
```
### Anotaciones para Java
```kotlin
// @JvmField exposes property as field
class User {
@JvmField
val id: Int = 0
}
// @JvmName specifies Java method name
@JvmName("getUsersByRole")
fun getUsers(role: String) { }
// @JvmStatic for companion object methods
class Utils {
companion object {
@JvmStatic
fun helper() {}
}
}
// @Throws for checked exceptions
@Throws(IOException::class)
fun readFile(path: String): String { }
```
## Desarrollo Android
### View Binding
```kotlin
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.button.setOnClickListener {
binding.textView.text = "Clicked!"
}
}
}
```
### Corrutinas con ViewModel
```kotlin
class MyViewModel : ViewModel() {
private val _data = MutableLiveData()
val data: LiveData = _data
private val _scope = ViewModelScope(Dispatchers.Main)
fun loadData() {
_scope.launch {
val result = withContext(Dispatchers.IO) {
api.fetchData()
}
_data.value = result
}
}
}
```
### Funciones de Extensión para Vistas
```kotlin
fun View.visible() {
visibility = View.VISIBLE
}
fun View.gone() {
visibility = View.GONE
}
fun View.onClick(action: () -> Unit) {
setOnClickListener { action() }
}
// Usage
binding.button.visible()
binding.progressBar.gone()
binding.button.onClick { doSomething() }
```
### Kotlin Android Extensions
```kotlin
// build.gradle
// apply plugin: 'kotlin-android-extensions'
// In Activity
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button.setOnClickListener {
textView.text = "Hello!"
}
}
}
```
## Consejos de Rendimiento
### Evitar Crear Objetos Innecesarios
```kotlin
// Bad - creates String each iteration
val result = items.joinToString("") { it.value }
// Good - reuse separator
val result = items.joinToString("") { it.value }
```
### Usar inline para Funciones Pequeñas
```kotlin
inline fun List.first(predicate: (T) -> Boolean): T? {
for (element in this) {
if (predicate(element)) return element
}
return null
}
```
### Secuencia para Colecciones Grandes
```kotlin
// When processing large collections
val result = largeList
.asSequence()
.map { expensiveOperation(it) }
.filter { it > 0 }
.take(10)
.toList()
```
## Manejo de Errores
### Usar Tipo Result
```kotlin
fun divide(a: Int, b: Int): Result {
return if (b == 0) {
Result.failure(IllegalArgumentException("Cannot divide by zero"))
} else {
Result.success(a / b)
}
}
fun main() {
val result = divide(10, 2)
result.fold(
onSuccess = { println("Result: $it") },
onFailure = { println("Error: ${it.message}") }
)
}
```
### runCatching
```kotlin
fun main() {
val result = runCatching {
"hello".toInt()
}
println(result.isSuccess) // false
println(result.exceptionOrNull()) // NumberFormatException
}
```
## Pruebas
```kotlin
// Using JUnit 5 with Kotlin
class CalculatorTest {
private val calculator = Calculator()
@Test
fun `adds two numbers`() {
assertEquals(5, calculator.add(2, 3))
}
@Test
fun `divides two numbers`() {
assertThrows {
calculator.divide(1, 0)
}
}
}
```
## Resumen
- Sigue las convenciones de nomenclatura y formato de Kotlin
- Usa patrones de Kotlin idiomáticos (elvis, let, apply)
- Las data classes reducen el código repetitivo
- Encadena operaciones de colecciones en lugar de bucles
- Usa anotaciones @Jvm para interoperabilidad con Java
- El desarrollo Android se beneficia de extensiones y corrutinas
- Usa secuencias para procesamiento de colecciones grandes
- Tipos Result y runCatching para manejo de errores
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →