miércoles, 23 de agosto de 2017

Android y Git. Disponer del hash automáticamente.


Una de las cosas a las que estoy acostumbrado, es tener siempre en mi código, el hash/tag/versión del control de versiones que estoy usando, para poder rápidamente identificar esa aplicación en 
que parte se encuentra dentro de mi historia.

Mucha gente hace una variable , ya sea de fecha de compilacion, hora, etc, o var = "1.0.a".
Pero la idea es que sea el propio entorno el que lo haga por nosotros.

Para dotar a Android Studio de ello, a través de gradle, tan simple como editar el fichero 
build.gradle


apply plugin: 'com.android.application'

android {
    def getGitHash = { ->
        def stdout = new ByteArrayOutputStream()
        exec {
            commandLine 'git', 'rev-parse', '--short', 'HEAD'
            standardOutput = stdout
        }
        return stdout.toString().trim()
    }
    compileSdkVersion 25
    buildToolsVersion '25.0.0'
    defaultConfig {
        applicationId 'com.app.com.test'
        minSdkVersion 19
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        buildConfigField "String", "GitHash", "\"${getGitHash()}\""
    }

Lo marcado en color es lo que tenéis que poner en el fichero build.gradle.
OJO , la función getGitHash() DEBE de estar ARRIBA, de lo contrario, no la encontrará.

Después , para usarla desde nuestro código;
String hash = BuildConfig.GitHash;


sábado, 19 de agosto de 2017

KOTLIN. Property , esa cosa que no es un field.


KOTLIN. Property , esa cosa que no es un field.


Haciendo una pausa en la documentación, me pongo a experimental el crear / pasar mis clases de cálculos mecánicos  desde Java a Kotlin, y me encuentro que estoy encallado a las primeras de cambio.

Para ello voy a poner la clase muy simplificada en Java , su conversión a Kotlin , y una breve explicación del problema encontrado;

Platform declaration clash: The following declarations have the same JVM signature (getDiametroExterior()D):
public final fun <get-diametroExterior>(): Double
public final fun getDiametroExterior(): Double

La clase en Java ;


public class Pieza {
       private double diametroExterior;
       public Pieza(){
            reset();
       }
       public void reset(){
           setDiametroExterior(0); 
       }
       public double getDiametroExterior() {
           return diametroExterior;        }
       public void setDiametroExterior(double diametroExterior) {
           this.diametroExterior = diametroExterior;
       }
}


Podemos observa que tenemos un field, llamado diametroExterior, y sus getters/setters correspondientes.

Si intentamos convertir esto tal cual a Kotlin, con mentalidad "Javato" ;-)

open class PiezaK {

    init {
        reset()
    }

    var diametroExterior: Double = 0.0

    fun reset() {
        setDiametroExterior(0.0)        
    }

    fun getDiametroExterior(): Double {
         return diametroExterior
    }

    fun setDiametroExterior(diametroExterior: Double) {
         this.diametroExterior = diametroExterior
    }
}

Es en este caso cuando nos dará el mensaje  "...The following declarations have the same JVM signature..."

¿ Pero donde  está el problema ? 
Bueno, una manera rápida de solucionarlo sería hacer lo siguiente, declarar la variable como private.  De esta manera solucionamos la conversión de Java a Kotlin. 

¿ Es correcto ? NO. ¿ Funciona ? SI, ¿ Entonces donde radica el problema ?

El problema es entender que en Kotlin NO EXISTE los fields de Java.

Esta declaración de Java private double diametroExterior no equivale a  var diametroExterior: Double en Kotlin

En Java es un field, en Kotlin es una property
¿ Y que hace Kotlin automáticamente por nosotros ? Si, los getters/setters.

Por eso, cuando es private funciona para acceder a esa propiedad, pero cuando se indica que es public estamos reescribiendo las funciones que Kotlin hace por nosotros.

Por lo tanto, la transformación correcta sería;


open class PiezaK {
    init {       reset()    }
    var diametroExterior: Double = 0.0   
    fun reset() {
       diametroExterior = 0
    }
}


Es decir, cuando se declara una property, 

var  name = "PEPE"

realmente ocurre lo siguiente, el equivalente de esto en Java ;

private String _name = "PEPE";
public String getName() { return _name ; }
public void setName( String name ) { this._name = name }

Se puede ver como existe un campo oculto , en este caso _name , en que si tan siquiera sabe de su existencia.

Vamos, en como toda la vida en Clipper/Harbour con los SETGET :-)

Pero entonces , ¿ como hago mi get/set particular ? Muy fácil, pero lo veremos en otro post.

viernes, 18 de agosto de 2017

KOTLIN. Propiedades y campos

Propiedades y campos

Declaración de propiedades


Las clases en Kotlin pueden tener características. Estos pueden ser declarados como mutable, utilizando la palabra clave var o de sólo lectura utilizando la palabra clave val.
class Address {
   var name: String = ...
   var street: String = ...
   var city: String = ...
   var state: String? = ...
   var zip: String = ...
}
Para usar una propiedad, simplemente nos referimos a ella por su nombre, como si fuera un campo en Java:

fun copyAddress(address: Address): Address {
   val result = Address() // there's no 'new' keyword in Kotlin
   result.name = address.name // accessors are called
   result.street = address.street
   // ...
   return result
}

Getters y Setters
La sintaxis completa para declarar una property es

var <propertyName>[: <PropertyType>] [= <property_initializer>]
   [<getter>]
   [<setter>]

El inicializador, getter y setter son opcionales. El tipo de propiedad es opcional si se puede inferir desde el inicializador (o desde el tipo de retorno getter, como se muestra a continuación).

var allByDefault: Int? // Error: Inicializador explícito requerido, getter y setter implícito
var initialized = 1 // Tiene tipo  Int, por defecto getter y setter

La sintaxis completa de una declaración de propiedad de sólo lectura difiere de una mutable de dos maneras: comienza con val en lugar de var y no permite a un setter:

val simple: Int? // has type Int, default getter, must be initialized in constructor
val inferredType = 1 // has type Int and a default getter

Podemos escribir descriptores personalizados, muy parecidos a las funciones ordinarias, dentro de una declaración de propiedad. He aquí un ejemplo de un getter personalizado:

val isEmpty: Boolean
   get() = this.size == 0

Un setter personalizado se ve así:

var stringRepresentation: String
   get() = this.toString()
   set(value) {
       setDataFromString(value) // parses the string and assigns values to other properties
   }

Por convención, el nombre del parámetro setter es value, pero puede elegir un nombre diferente si lo prefiere.

Desde Kotlin 1.1, puede omitir el tipo de property sí se puede inferir desde el getter:

val isEmpty get() = this.size == 0  // por inferencia, tipo Boolean

Si necesita cambiar la visibilidad de un descriptor o anotarlo, pero no necesita cambiar la implementación predeterminada, puede definir el descriptor sin definir su cuerpo:

var setterVisibility: String = "abc"
   private set // the setter is private and has the default implementation

var setterWithAnnotation: Any? = null
   @Inject set // annotate the setter with Inject


Campos de apoyo

Las clases en Kotlin no pueden tener campos. Sin embargo, a veces es necesario tener un campo de respaldo cuando se utilizan descriptores personalizados. Para estos propósitos, Kotlin proporciona un campo de respaldo automático al que se puede acceder mediante el identificador de field:

var counter = 0 // the initializer value is written directly to the backing field
   set(value) {
       if (value >= 0) field = value
   }

El identificador de field sólo se puede utilizar en los accesores de la propiedad. Se generará un campo de respaldo para una propiedad si utiliza la implementación predeterminada de al menos uno de los accesores o si un accesor personalizado lo hace a través del identificador de campo.

Por ejemplo, en el caso siguiente no habrá campo de respaldo:

val isEmpty: Boolean
   get() = this.size == 0


Propiedades de apoyo

Si desea hacer algo que no encaja en este esquema de campo de respaldo implícito, siempre puede volver a tener una propiedad de respaldo:

private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
   get() {
       if (_table == null) {
           _table = HashMap() // Type parameters are inferred
       }
       return _table ?: throw AssertionError("Set to null by another thread")
   }

En todos los aspectos, esto es lo mismo que en Java, ya que el acceso a propiedades privadas con getters y setters predeterminados está optimizado para que no se introduzca ninguna sobrecarga de llamada de función.


Constante de tiempo de compilación

Las propiedades cuyo valor se conoce en tiempo de compilación se pueden marcar como constantes de tiempo de compilación usando el modificador const. Tales propiedades deben cumplir los siguientes requisitos:

  • Nivel superior o miembro de un objeto
  • Inicializado con un valor del tipo String o un tipo primitivo
  • Ningún getter personalizado

Tales propiedades se pueden utilizar en anotaciones:

const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"

@Deprecated(SUBSYSTEM_DEPRECATED) fun foo() { ... }


Propiedades con inicio tardío

Normalmente, las propiedades declaradas como de tipo no nulo deben ser inicializadas en el constructor. Sin embargo, con bastante frecuencia esto no es conveniente. Por ejemplo, las propiedades se pueden inicializar mediante inyección de dependencia o en el método de configuración de una prueba de unitaria. En este caso, no puede proporcionar que no sea NULL en el constructor, pero aún así desea evitar las comprobaciones nulas al referenciar la propiedad dentro del cuerpo de una clase.
Para manejar este caso, puede marcar la propiedad con el modificador lateinit:

public class MyTest {
   lateinit var subject: TestSubject

   @SetUp fun setup() {
       subject = TestSubject()
   }

   @Test fun test() {
       subject.method()  // dereference directly
   }
}

El modificador sólo se puede utilizar en propiedades var declaradas dentro del cuerpo de una clase (no en el constructor principal), y sólo cuando la propiedad no tiene un getter o Setter personalizado. El tipo de la propiedad debe ser no nulo y no debe ser un tipo primitivo.

El acceso a una propiedad lateinit antes de que se haya inicializado lanza una excepción especial que identifica claramente la propiedad a la que se accede y el hecho de que no se ha inicializado.

Sobrecarga de Propiedades


Propiedades delegadas

El tipo más común de propiedades simplemente lee desde (y tal vez escribe a) un campo de respaldo. Por otro lado, con getters y setters personalizados se puede implementar cualquier comportamiento de una propiedad. En algún lugar en el medio, hay ciertos patrones comunes de cómo una propiedad puede funcionar. Algunos ejemplos: valores perezosos, lectura de un mapa por una clave determinada, acceso a una base de datos, notificando al oyente en el acceso, etc.

Tales comportamientos comunes se pueden implementar como bibliotecas usando propiedades delegadas, que veremos mśa tarde.

Android y Git. Disponer del hash automáticamente.

Una de las cosas a las que estoy acostumbrado, es tener siempre en mi código, el hash/tag/versión del control de versiones que estoy usan...