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.

martes, 15 de agosto de 2017

KOTLIN. Clases y Herencia.

En una serie de post, iré publicando en español, la documentación sobre KOTLIN,
referente a la Programación Orientado a Objetos.

Documentación y traducción sobre https://kotlinlang.org/docs/reference/classes.html

CLASES Y HERENCIA

Para crear una clase en Kotlin, simplemente usaremos la palabra reservada, class

class Invoice {
}

La declaración de clase consiste en el nombre de clase, el encabezado de clase (especificando sus parámetros de tipo, el constructor principal, etc.) y el cuerpo de clase, rodeado de llaves. Tanto el encabezado como el cuerpo son opcionales; Si la clase no tiene cuerpo, las llaves pueden ser omitidos.

class Vacia

Constructores

Una clase en Kotlin puede tener un constructor principal y uno o más constructores secundarios. El constructor primario es parte del encabezado de clase: va después del nombre de clase (y los parámetros de tipo opcional).
class Person constructor(firstName: String) {
}
Si el constructor principal no tiene ninguna anotación o modificadores de visibilidad, se puede omitir la palabra clave constructor

class Person(firstName: String) {
}

El constructor principal no puede contener ningún código. El código de inicialización se puede colocar en los bloques de inicialización, que están prefijados con la palabra clave init:

class Customer(name: String, phone: String) {
   init {
       logger.info("Customer initialized with value ${name}")
   }
}

Tenga en cuenta que los parámetros del constructor primario se pueden utilizar en los bloques de inicialización. También se pueden utilizar en los inicializadores de propiedades declarados en el cuerpo de la clase:

class Customer(name: String) {
   val customerKey = name.toUpperCase()
}

De hecho, para declarar propiedades e inicializarlas desde el constructor primario, Kotlin tiene una sintaxis concisa:

class Person(val firstName: String, val lastName: String, var age: Int) {
   // ...
}

De la misma manera que las propiedades regulares, las propiedades declaradas en el constructor primario pueden ser mutable (var) o de sólo lectura (val)

Si el constructor tiene anotaciones o modificadores de visibilidad, se requiere la palabra clave constructor, y los modificadores van antes de ella:

class Customer public @Inject constructor(name: String) { ... }

Constructores secundarios

La clase también puede declarar constructores secundarios, que están prefijados con constructor
class Person {
   constructor(parent: Person) {
       parent.children.add(this)
   }
}

Si la clase tiene un constructor primario, cada constructor secundario necesita delegar al constructor principal, directa o indirectamente a través de otro constructor (s) secundario (s). La delegación a otro constructor de la misma clase se realiza mediante la palabra clave this:
class Person(val name: String) {
   constructor(name: String, parent: Person) : this(name) {
       parent.children.add(this)
   }
}

Si una clase no abstracta no declara ningún constructor (primario o secundario), tendrá un constructor primario generado sin argumentos. La visibilidad del constructor será pública. Si no desea que su clase tenga un constructor público, debe declarar un constructor primario vacío con visibilidad no predeterminada:

class DontCreateMe private constructor () {
}

NOTA: En la JVM, si todos los parámetros del constructor principal tienen valores predeterminados, el compilador generará un constructor adicional sin parámetros que utilizará los valores predeterminados. Esto facilita el uso de Kotlin con bibliotecas como Jackson o JPA que crean instancias de clase a través de constructores sin parámetro

class Customer(val customerName: String = "")

Creación de instancias de clases

Para crear una instancia de una clase, llamamos al constructor como si fuera una función regular:

val invoice = Invoice()

val customer = Customer("Joe Smith")

Tenga en cuenta que Kotlin no tiene una palabra clave new. La creación de instancias de clases internas anidadas, internas y anónimas se describe en Clases anidadas

Miembros del grupo Las clases pueden contener Constructores y bloques de inicialización Funciones Propiedades Clases anidadas e internas Declaraciones de objetos

Miembros de la clase

Las clases pueden contener
  • Constructores y bloques de inicialización
  • Funciones
  • Propiedades
  • Clases anidadas e internas
  • Declaraciones de objetos

Herencia

Todas las clases en Kotlin tienen una superclase común Any, que es un super por defecto para una clase sin supertipos declarados:

class Example // Implícitamente hereda de Any

Any no es java.lang.Object; En particular, no tiene ningún miembro que no sean equals(), hashCode() y toString(). Consulte la sección de interoperabilidad de Java para obtener más detalles.

Para declarar un supertipo explícito, colocamos el tipo después de dos puntos en el encabezado de la clase:

open class Base(p: Int)

class Derived(p: Int) : Base(p)

Si la clase tiene un constructor primario, el tipo de base puede (y debe) ser inicializado allí mismo, usando los parámetros del constructor primario.

Si la clase no tiene un constructor principal, cada constructor secundario tiene que inicializar el tipo base utilizando la palabra clave super o delegar a otro constructor que lo haga. Tenga en cuenta que en este caso diferentes constructores secundarios pueden llamar a diferentes constructores del tipo base:

class MyView : View {
   constructor(ctx: Context) : super(ctx)

   constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}
  
La anotación open en una clase es lo opuesto al final de Java: permite que otros hereden de esta clase.
Por defecto, todas las clases en Kotlin son final, lo que corresponde a  Effective Java, Item 17: Design and document for inheritance or else prohibit it.

Por lo tanto, para que una clase pueda ser heredable, hay que indicarlo
explícitamente con open

Sobrecarga de methods

Como mencionamos antes, nos apegamos a hacer las cosas explícitas en Kotlin. Y a diferencia de Java, Kotlin requiere anotaciones explícitas para los miembros overridable (los llamamos open) y para overrides:

open class Base {
   open fun v() {}
   fun nv() {}
}
class Derived() : Base() {
   override fun v() {}
}

La anotación de override es necesaria para Derived.v (). Si eso falta, el compilador se quejaría. Si no hay anotación open en una función, como Base.nv (), declarar un método con la misma firma en una subclase es ilegal, ya sea con override o sin ella. En una clase final (por ejemplo, una clase sin anotación open), los miembros abiertos están prohibidos.

Un miembro marcado override está abierto, es decir, puede ser anulado en subclases. Si desea prohibir el reemplazo, use final:

open class AnotherDerived() : Base() {
   final override fun v() {}
}

Sobrecarga de propiedades

Las sobrecarga de propiedades funcionan de manera similar a los métodos sobrecargados; las propiedades declaradas en una superclase que luego se re-declaran en una clase derivada deben ser prefaciadas con override y deben tener un tipo compatible.  Cada propiedad declarada puede ser anulada por una propiedad con un inicializador o por una propiedad con un método getter.

open class Foo {
   open val x: Int get { ... }
}

class Bar1 : Foo() {
   override val x: Int = ...
}

También puede anular(override) una propiedad val con una propiedad var, pero no viceversa. Esto se permite porque una propiedad val esencialmente declara un método getter, y reemplazarlo como una var, además, declara un método setter en la clase derivada.

Tenga en cuenta que puede utilizar la palabra clave override como parte de la declaración de propiedad en un constructor principal.

interface Foo {
   val count: Int
}

class Bar1(override val count: Int) : Foo

class Bar2 : Foo {
   override var count: Int = 0
}

Llamando a la implementación de la SuperClass

Código en una clase derivada puede llamar a sus funciones de superclase y las implementaciones de los accesores de propiedad usando la palabra clave super:

open class Foo {
   open fun f() { println("Foo.f()") }
   open val x: Int get() = 1
}

class Bar : Foo() {
   override fun f() {
       super.f()
       println("Bar.f()")
   }
   
   override val x: Int get() = super.x + 1
}

Dentro de una clase interna, acceder a la superclase de la clase externa se hace
con la palabra clave super calificada con el nombre de clase exterior: super@Externa:
class Bar : Foo() {
   override fun f() { /* ... */ }
   override val x: String get() = "..."
   inner class Baz {
       fun g() {
           super@Bar.f() // Calls Foo's implementation of f()
           println(super@Bar.x) // Uses Foo's implementation of x's getter
       }
   }
}
Reglas primordiales
En Kotlin, la herencia de implementación está regulada por la siguiente regla: si una clase hereda muchas implementaciones del mismo miembro de sus superclases inmediatas, debe reemplazar a este miembro y proporcionar su propia implementación (tal vez usando uno de los heredados). Para denotar el supertipo del cual se toma la implementación heredada, usamos super calificado por el nombre de supertipo en corchetes angulares, p. Super <Base>:
open class A {
   open fun f() { print("A") }
   fun a() { print("a") }
}

interface B {
   fun f() { print("B") } // interface members are 'open' by default
   fun b() { print("b") }
}

class C() : A(), B {
   // The compiler requires f() to be overridden:
   override fun f() {
       super<A>.f() // call to A.f()
       super<B>.f() // call to B.f()
   }
}
Está bien heredar de A y B, y no tenemos problemas con a () y b () ya que C hereda una sola implementación de cada una de estas funciones. Pero para f () tenemos dos implementaciones heredadas por C, y por lo tanto tenemos que sobreescribir f () en C y proporcionar nuestra propia implementación que elimina la ambigüedad.

Clases Abstractas
Una clase y algunos de sus miembros pueden ser declarados abstractos, abstract. Un miembro abstracto no tiene una implementación en su clase. Tenga en cuenta que no necesitamos anotar una clase o una función abstracta con open - no hace falta decirlo.
Podemos sobreescribir un miembro, override,  abierto no abstracto con una abstracta;
open class Base {
   open fun f() {}
}

abstract class Derived : Base() {
   override abstract fun f()
}
Objetos complementarios
En Kotlin, a diferencia de Java o C #, las clases no tienen métodos estáticos. En la mayoría de los casos, se recomienda simplemente usar funciones a nivel de paquete. Si necesita escribir una función que puede llamarse sin tener una instancia de clase pero necesita acceso a los elementos internos de una clase (por ejemplo, un factory method), puede escribirla como miembro de una declaración de objeto dentro de esa clase.
Incluso más específicamente, si declara un objeto complementario dentro de su clase, podrá llamar a sus miembros con la misma sintaxis que llamar a métodos estáticos en Java / C #, utilizando sólo el nombre de clase como calificador.

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 mi...