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.

No hay comentarios:

Publicar un comentario

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