Kotlin Clases y Objetos
Definición de clases
Las clases de Kotlin pueden contener: constructores y bloques de código de inicialización, funciones, propiedades, clases internas y declaraciones de objetos.
En Kotlin se usa la palabra claveclasspara declarar una clase, seguida del nombre de la clase:
class Ejemplo { // 类名为 Ejemplo
// 大括号内是类体构成
}
También podemos definir una clase vacía:
class Empty
Se pueden definir funciones miembro en la clase:
class Ejemplo() {
fun foo() { print("Foo") } // 成员函数
}
Propiedades de la clase
Definición de propiedades
Las propiedades de la clase se pueden declarar con la palabra clavevarpara declararlas mutables; de lo contrario, se usa la palabra clave de solo lecturavalpara declararlas inmutables.
class Ejemplo {
var name: String = ……
var url: String = ……
var city: String = ……
}
Podemos crear una instancia de la clase usando el constructor como si fuera una función normal:
val site = Ejemplo() // Kotlin 中没有 new 关键字
Para usar una propiedad, basta con referenciarla por su nombre.
site.name // 使用 . 号来引用 site.url
Las clases en Kotlin pueden tener un constructor principal y uno o más constructores secundarios. El constructor principal es parte del encabezado de la clase, ubicado después del nombre de la clase:
class Person constructor(firstName: String) {}
Si el constructor principal no tiene anotaciones ni modificadores de visibilidad, la palabra clave `constructor` se puede omitir.
class Person(firstName: String) {
}
getters y setters
Sintaxis completa de la declaración de propiedades:
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
Tanto getter como setter son opcionales.
Si el tipo de la propiedad se puede inferir de la declaración de inicialización o de las funciones miembro de la clase, se puede omitir el tipo. `val` no permite definir una función setter porque es de solo lectura.
var allByDefault: Int? // 错误: 需要一个初始化语句, 默认实现了 getter 和 setter 方法 var initialized = 1 // 类型为 Int, 默认实现了 getter 和 setter val simple: Int? // 类型为 Int ,默认实现 getter ,但必须在构造函数中初始化 val inferredType = 1 // 类型为 Int 类型,默认实现 getter
Ejemplo
El siguiente ejemplo define una clase `Person` con dos variables mutables `lastName` y `no`. `lastName` modifica el método getter y `no` modifica el método setter.
class Person {
var lastName: String = "zhang"
get() = field.toUpperCase() // 将变量赋值后转换为大写
set
var no: Int = 100
get() = field // 后端变量
set(value) {
if (value < 10) { // 如果传入的值小于 10 返回该值
field = value
} else {
field = -1 // 如果传入的值大于等于 10 返回 -1
}
}
var heiht: Float = 145.4f
private set
}
// 测试
fun main(args: Array<String>) {
var person: Person = Person()
person.lastName = "wang"
println("lastName:${person.lastName}")
person.no = 9
println("no:${person.no}")
person.no = 20
println("no:${person.no}")
}
El resultado es:
lastName:WANG no:9 no:-1
Las clases en Kotlin no pueden tener fields (campos). Sin embargo, a veces, al usar accesores personalizados, se necesita un backing field. Para ello, Kotlin proporciona un backing field automático, que puede accederse mediante el identificador `field`.
Backing Field es un concepto en Kotlin. Es un campo generado automáticamente que se utiliza para almacenar el valor de la propiedad. Solo se puede usar dentro de los accesores (getter o setter) y solo existe si se utiliza la implementación predeterminada de al menos un accesor, o si un accesor personalizado lo referencia mediante el identificador `field`. Esto significa que, cuando una propiedad necesita un backing field, Kotlin lo proporciona automáticamente. Puede usar el identificador `field` para referenciar el backing field dentro de los accesores.
var no: Int = 100
get() = field // 后端变量
set(value) {
if (value < 10) { // 如果传入的值小于 10 返回该值
field = value
} else {
field = -1 // 如果传入的值大于等于 10 返回 -1
}
}
Por supuesto, aquí hay un ejemplo de backing field:
class Person {
var name: String = "initial value"
set(value) {
if (value.isNotEmpty()) {
field = value
}
}
}
En este ejemplo, la propiedad `name` tiene un setter personalizado que verifica si el valor entrante está vacío. Si no está vacío, se usa el identificador `field` para asignar el valor al backing field. De esta manera, cuando intentamos asignar una cadena vacía a la propiedad `name`, su valor no cambia.
val person = Person() println(person.name) // 输出 "initial value" person.name = "" println(person.name) // 仍然输出 "initial value" person.name = "John" println(person.name) // 输出 "John"
En el código anterior, creamos una instancia de la clase `Person` e intentamos cambiar el valor de su propiedad `name`. Cuando intentamos asignarle una cadena vacía, debido a la verificación del setter personalizado, el valor de `name` permanece igual. Cuando le asignamos una cadena no vacía, su valor se cambia exitosamente al nuevo valor.
Las propiedades no nulas deben inicializarse en el momento de la definición. Kotlin ofrece una solución de inicialización diferida, usando la palabra clavelateinitpara declarar la propiedad:
public class MyTest {
lateinit var subject: TestSubject
@SetUp fun setup() {
subject = TestSubject()
}
@Test fun test() {
subject.method() // dereference directly
}
}
Constructor principal
El constructor principal no puede contener ningún código. El código de inicialización puede colocarse en un bloque de inicialización, que está prefijado con la palabra clave `init`.
class Person constructor(firstName: String) {
init {
println("FirstName is $firstName")
}
}
Nota: los parámetros del constructor principal se pueden usar en el bloque de inicialización, o en el código de inicialización de las propiedades definidas en el cuerpo de la clase. Una sintaxis concisa permite definir propiedades e inicializar sus valores mediante el constructor principal (pueden ser `var` o `val`):
class People(val firstName: String, val lastName: String) {
//...
}
Si el constructor tiene anotaciones o modificadores de visibilidad, la palabra clave `constructor` es obligatoria, y las anotaciones y modificadores deben colocarse antes de ella.
Ejemplo
Crea una clase `Ejemplo` y pasa el nombre del sitio web a través del constructor:
class Ejemplo constructor(name: String) { // 类名为 Ejemplo
// 大括号内是类体构成
var url: String = "http://www.example.com"
var country: String = "CN"
var siteName = name
init {
println("初始化网站名: ${name}")
}
fun printTest() {
println("我是类的函数")
}
}
fun main(args: Array<String>) {
val ejemplo = Ejemplo("Ejemplo")
println(ejemplo.siteName)
println(ejemplo.url)
println(ejemplo.country)
ejemplo.printTest()
}
El resultado es:
初始化网站名: Ejemplo Ejemplo http://www.example.com CN 我是类的函数
Constructores secundarios
Las clases también pueden tener constructores secundarios, que requieren el prefijo `constructor`:
class Person {
constructor(parent: Person) {
parent.children.add(this)
}
}
Si la clase tiene un constructor principal, cada constructor secundario debe delegar en el constructor principal, ya sea directa o indirectamente, a través de otro constructor secundario. Para delegar en otro constructor de la misma clase se usa la palabra clave `this`:
class Person(val name: String) {
constructor (name: String, age:Int) : this(name) {
// 初始化...
}
}
Si una clase no abstracta no declara ningún constructor (principal o secundario), se genera un constructor sin parámetros. El constructor es `public`. Si no quieres que tu clase tenga un constructor público, debes declarar un constructor principal vacío:
class DontCreateMe private constructor () {
}
Nota: en la máquina virtual JVM, si todos los parámetros del constructor principal tienen valores por defecto, el compilador generará un constructor adicional sin parámetros que usará directamente los valores por defecto. Esto permite a Kotlin utilizar más fácilmente librerías como Jackson o JPA que crean instancias de clases mediante constructores sin parámetros.
class Customer(val customerName: String = "")
Ejemplo
class Ejemplo constructor(name: String) { // 类名为 Ejemplo
// 大括号内是类体构成
var url: String = "http://www.example.com"
var country: String = "CN"
var siteName = name
init {
println("初始化网站名: ${name}")
}
// 次构造函数
constructor (name: String, alexa: Int) : this(name) {
println("Alexa 排名 $alexa")
}
fun printTest() {
println("我是类的函数")
}
}
fun main(args: Array<String>) {
val ejemplo = Ejemplo("Ejemplo", 10000)
println(ejemplo.siteName)
println(ejemplo.url)
println(ejemplo.country)
ejemplo.printTest()
}
El resultado es:
初始化网站名: Ejemplo Alexa 排名 10000 Ejemplo http://www.example.com CN 我是类的函数
Clases abstractas
La abstracción es una de las características de la programación orientada a objetos. La clase en sí, o algunos miembros de la clase, pueden declararse como `abstract`. Los miembros abstractos no tienen una implementación concreta en la clase.
Nota: no es necesario anotar las clases o miembros abstractos con `open`.
open class Base {
open fun f() {}
}
abstract class Derived : Base() {
override abstract fun f()
}
Clases anidadas
Podemos anidar clases dentro de otras clases; vea el siguiente ejemplo:
class Outer { // 外部类
private val bar: Int = 1
class Nested { // 嵌套类
fun foo() = 2
}
}
fun main(args: Array<String>) {
val demo = Outer.Nested().foo() // 调用格式:外部类.嵌套类.嵌套类方法/属性
println(demo) // == 2
}
Clases internas
Las clases internas se indican con la palabra clave `inner`.
Una clase interna lleva una referencia al objeto de la clase externa, por lo que puede acceder a las propiedades y funciones miembro de la clase externa.
class Outer {
private val bar: Int = 1
var v = "成员属性"
/**嵌套内部类**/
inner class Inner {
fun foo() = bar // 访问外部类成员
fun innerTest() {
var o = this@Outer //获取外部类的成员变量
println("内部类可以引用外部类的成员,例如:" + o.v)
}
}
}
fun main(args: Array<String>) {
val demo = Outer().Inner().foo()
println(demo) // 1
val demo2 = Outer().Inner().innerTest()
println(demo2) // 内部类可以引用外部类的成员,例如:成员属性
}
Para eliminar la ambigüedad, al acceder al `this` del ámbito externo, usamos `this@label`, donde `@label` es una etiqueta que se refiere al origen del `this`.
Clases internas anónimas
Se utilizan expresiones de objeto para crear clases internas anónimas:
class Test {
var v = "成员属性"
fun setInterFace(test: TestInterFace) {
test.test()
}
}
/**
* 定义接口
*/
interface TestInterFace {
fun test()
}
fun main(args: Array<String>) {
var test = Test()
/**
* 采用对象表达式来创建接口对象,即匿名内部类的实例。
*/
test.setInterFace(object : TestInterFace {
override fun test() {
println("对象表达式创建匿名内部类的实例")
}
})
}
Modificadores de clase
Los modificadores de clase incluyen `classModifier` y `accessModifier`:
`classModifier`: modificador de atributos de clase, indica las características de la propia clase.
abstract // 抽象类 final // 类不可继承,默认属性 enum // 枚举类 open // 类可继承,类默认是final的 annotation // 注解类
`accessModifier`: modificador de acceso
private // 仅在同一个文件中可见 protected // 同一个文件中或子类可见 public // 所有调用的地方都可见 internal // 同一个模块中可见
Ejemplo
// 文件名:example.kt
package foo
private fun foo() {} // 在 example.kt 内可见
public var bar: Int = 5 // 该属性随处可见
internal val baz = 6 // 相同模块内可见
Otras extensiones