Classes et objets en Kotlin
Définition de classe
Les classes Kotlin peuvent contenir : des constructeurs et des blocs d'initialisation, des fonctions, des propriétés, des classes internes et des déclarations d'objets.
En Kotlin, le mot-cléclassdéclare une classe, immédiatement suivi du nom de la classe :
class Example { // 类名为 Example
// 大括号内是类体构成
}
Nous pouvons également définir une classe vide :
class Empty
Les fonctions membres peuvent être définies dans la classe :
class Example() {
fun foo() { print("Foo") } // 成员函数
}
Propriétés de classe
Définition de propriété
Les propriétés de classe peuvent utiliser le mot-clévarpour déclarer une propriété modifiable ; sinon, utilisez le mot-clé en lecture seulevalpour déclarer une propriété immuable.
class Example {
var name: String = ……
var url: String = ……
var city: String = ……
}
Nous pouvons créer des instances de classe à l'aide de constructeurs, tout comme des fonctions ordinaires :
val site = Example() // Kotlin 中没有 new 关键字
Pour utiliser une propriété, il suffit de la référencer par son nom
site.name // 使用 . 号来引用 site.url
Une classe en Kotlin peut avoir un constructeur primaire et un ou plusieurs constructeurs secondaires. Le constructeur primaire fait partie de l'en-tête de la classe et se situe après le nom de la classe :
class Person constructor(firstName: String) {}
Si le constructeur primaire n'a aucune annotation ni modificateur de visibilité, le mot-clé constructor peut être omis.
class Person(firstName: String) {
}
getters et setters
La syntaxe complète pour déclarer une propriété est :
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
Le getter et le setter sont tous les deux facultatifs.
Si le type de la propriété peut être déduit de l'initialiseur ou des fonctions membres de la classe, le type peut être omis. val n'autorise pas de fonction setter car il est en lecture seule.
var allByDefault: Int? // 错误: 需要一个初始化语句, 默认实现了 getter 和 setter 方法 var initialized = 1 // 类型为 Int, 默认实现了 getter 和 setter val simple: Int? // 类型为 Int ,默认实现 getter ,但必须在构造函数中初始化 val inferredType = 1 // 类型为 Int 类型,默认实现 getter
Exemple
L'exemple suivant définit une classe Person avec deux variables mutables, lastName et no. lastName redéfinit la méthode getter, et no redéfinit la méthode 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}")
}
La sortie est :
lastName:WANG no:9 no:-1
Les classes en Kotlin ne peuvent pas avoir de champs. Cependant, un champ de stockage est parfois nécessaire lors de l'utilisation d'accesseurs personnalisés. À cette fin, Kotlin fournit un champ de stockage automatique, auquel on peut accéder à l'aide de l'identifiant field.
Un champ de stockage est un concept en Kotlin. C'est un champ généré automatiquement qui sert à stocker la valeur d'une propriété. Il ne peut être utilisé qu'à l'intérieur d'accesseurs (getter ou setter), et il n'existe que si au moins un accesseur utilise l'implémentation par défaut, ou si un accesseur personnalisé y fait référence via l'identifiant field. Cela signifie que lorsqu'une propriété a besoin d'un champ de stockage, Kotlin le fournit automatiquement. Vous pouvez utiliser l'identifiant field pour référencer le champ de stockage à l'intérieur d'un accesseur.
var no: Int = 100
get() = field // 后端变量
set(value) {
if (value < 10) { // 如果传入的值小于 10 返回该值
field = value
} else {
field = -1 // 如果传入的值大于等于 10 返回 -1
}
}
Bien sûr, voici un exemple de champ de stockage :
class Person {
var name: String = "initial value"
set(value) {
if (value.isNotEmpty()) {
field = value
}
}
}
Dans cet exemple, la propriété name possède un setter personnalisé qui vérifie si la valeur entrante est vide. Si elle n'est pas vide, il utilise l'identifiant field pour affecter la valeur au champ de stockage. Ainsi, lorsque nous essayons d'affecter une chaîne vide à la propriété name, sa valeur ne change pas.
val person = Person() println(person.name) // 输出 "initial value" person.name = "" println(person.name) // 仍然输出 "initial value" person.name = "John" println(person.name) // 输出 "John"
Dans le code ci-dessus, nous avons créé une instance de la classe Person et avons essayé de modifier la valeur de sa propriété name. Lorsque nous avons essayé de lui affecter une chaîne vide, en raison de la vérification du setter personnalisé, la valeur de la propriété name est restée inchangée. Lorsque nous lui avons affecté une chaîne non vide, sa valeur a bien été remplacée par la nouvelle valeur.
Les propriétés non nulles doivent être initialisées au moment de la définition. Kotlin offre un moyen de retarder l'initialisation, en utilisant le mot-clélateinitpour annoter la propriété :
public class MyTest {
lateinit var subject: TestSubject
@SetUp fun setup() {
subject = TestSubject()
}
@Test fun test() {
subject.method() // dereference directly
}
}
Constructeur primaire
Le constructeur primaire ne peut contenir aucun code. Le code d'initialisation peut être placé dans un bloc d'initialisation, qui est préfixé par le mot-clé init.
class Person constructor(firstName: String) {
init {
println("FirstName is $firstName")
}
}
Remarque : les paramètres du constructeur primaire peuvent être utilisés dans les blocs d'initialisation, ainsi que dans les initialiseurs de propriétés définis dans le corps de la classe. Une syntaxe concise vous permet de définir des propriétés et d'initialiser leurs valeurs via le constructeur primaire (peut être var ou val) :
class People(val firstName: String, val lastName: String) {
//...
}
Si le constructeur possède des annotations ou des modificateurs de visibilité, le mot-clé constructor est requis, et les annotations et modificateurs doivent être placés avant celui-ci.
Exemple
Créez une classe Example et passez le nom du site web via le constructeur :
class Example constructor(name: String) { // 类名为 Example
// 大括号内是类体构成
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 example = Example("Example")
println(example.siteName)
println(example.url)
println(example.country)
example.printTest()
}
La sortie est :
初始化网站名: Example Example http://www.example.com CN 我是类的函数
Constructeurs secondaires
Une classe peut également avoir des constructeurs secondaires, qui nécessitent le préfixe constructor :
class Person {
constructor(parent: Person) {
parent.children.add(this)
}
}
Si la classe a un constructeur primaire, chaque constructeur secondaire doit déléguer au constructeur primaire, soit directement, soit indirectement via un autre constructeur secondaire. Utilisez le mot-clé this pour déléguer à un autre constructeur de la même classe :
class Person(val name: String) {
constructor (name: String, age:Int) : this(name) {
// 初始化...
}
}
Si une classe non abstraite ne déclare aucun constructeur (primaire ou secondaire), elle générera un constructeur sans paramètres. Le constructeur est public. Si vous ne voulez pas que votre classe ait un constructeur public, vous devez déclarer un constructeur primaire vide :
class DontCreateMe private constructor () {
}
Remarque : sur la JVM, si tous les paramètres du constructeur primaire ont des valeurs par défaut, le compilateur générera un constructeur supplémentaire sans argument, qui utilisera directement les valeurs par défaut. Cela facilite l'utilisation de Kotlin avec des bibliothèques comme Jackson ou JPA, qui créent des instances de classe à l'aide de constructeurs sans argument.
class Customer(val customerName: String = "")
Exemple
class Example constructor(name: String) { // 类名为 Example
// 大括号内是类体构成
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 example = Example("Example", 10000)
println(example.siteName)
println(example.url)
println(example.country)
example.printTest()
}
La sortie est :
初始化网站名: Example Alexa 排名 10000 Example http://www.example.com CN 我是类的函数
Classes abstraites
L'abstraction est l'une des caractéristiques de la programmation orientée objet. Il est possible de déclarer abstraite une classe elle-même, ou certains membres d'une classe. Les membres abstraits n'ont pas d'implémentation concrète dans la classe.
Remarque : il n'est pas nécessaire d'annoter les classes abstraites ou les membres abstraits avec l'annotation open.
open class Base {
open fun f() {}
}
abstract class Derived : Base() {
override abstract fun f()
}
Classes imbriquées
Nous pouvons imbriquer des classes à l'intérieur d'autres classes. Voir l'exemple suivant :
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
}
Classes internes
Les classes internes sont marquées par le mot-clé inner.
Une classe interne porte une référence à un objet de la classe externe, de sorte que la classe interne peut accéder aux propriétés membres et aux fonctions membres de la classe externe.
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) // 内部类可以引用外部类的成员,例如:成员属性
}
Pour éliminer toute ambiguïté, lors de l'accès à this depuis la portée externe, nous utilisons this@label, où @label est une étiquette faisant référence à la source de this.
Classes internes anonymes
Utilisez des expressions object pour créer des classes internes anonymes :
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("对象表达式创建匿名内部类的实例")
}
})
}
Modificateurs de classe
Les modificateurs de classe incluent classModifier et _accessModifier_ :
classModifier : modificateur de propriété de classe, indiquant les caractéristiques propres de la classe.
abstract // 抽象类 final // 类不可继承,默认属性 enum // 枚举类 open // 类可继承,类默认是final的 annotation // 注解类
accessModifier : modificateur de droits d'accès
private // 仅在同一个文件中可见 protected // 同一个文件中或子类可见 public // 所有调用的地方都可见 internal // 同一个模块中可见
Exemple
// 文件名:example.kt
package foo
private fun foo() {} // 在 example.kt 内可见
public var bar: Int = 5 // 该属性随处可见
internal val baz = 6 // 相同模块内可见
Autres extensions