Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Example Analysis of attributes and Fields in Kotlin

2025-01-30 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

Shulou(Shulou.com)06/01 Report--

This article mainly introduces the example analysis of attributes and fields in Kotlin, which has a certain reference value, and interested friends can refer to it. I hope you will gain a lot after reading this article.

I. Overview

   has previously explained the use of classes and the relevant knowledge of attributes. Attributes and fields basically appear in a class. Attributes are explained in detail in the articles on variables and constants, which will be briefly introduced here.

1.1 declare attributes

Variables in the Java class are declared as member variables, while properties in the Kotlin class are declared as properties, and properties in the Kotlin class can be declared as mutable using the var keyword or read-only using the val keyword. The property in the class must initialize the value, or an error will be reported.

Class Person {

Val id: String = "0" / / immutable, with a value of 0

Var nameA: String? = "Android" / / variable, allowed to be null

Var age: Int = 22 / / variable, non-empty type

}

It was mentioned earlier that Kotlin can effectively solve the problem of null pointers. In fact, a distinction between nullable and non-nullable flags is added when defining types, such as after the types declared above. Indicates that the property can be empty, but not after the type? Indicates that the property cannot be empty. Such as name: String? Name in can be null,age: age in Int cannot be null. When in use, the compiler will judge whether the property can be nullable and tell the developer whether it needs to be handled, so as to avoid null pointer exceptions.

The properties in the class used in Kotlin are the same as in Java, referenced by the class name:

Var person = Person () / / instantiate person, there is no new keyword in Kotlin

View1.text = person.id / / call attribute

In fact, the attributes defined above are incomplete, and the property definitions in Java also involve the get () and set () methods, so how do you express them in Kotlin?

2. Getter () and Setter ()

Getter () in Kotlin corresponds to the get () function in Java, and setter () corresponds to the set () function in Java, but note that this is just the name of Kotlin, and the real words are get () and set ().

2.1 complete syntax

Get () and set () functions are generally not provided in ordinary classes in Kotlin, because they are basically not used in ordinary classes, which is the same as Java, but Java uses get () and set () functions when defining pure data classes, but Kotlin defines data classes and has implemented the get () and set () functions for us.

The complete syntax for declaring properties is as follows:

Var [:] [=]

[]

[]

This is the official standard grammar, let's translate it:

Var: = initialization value

Among them, the initializer (property_initializer), getter, and setter are all optional, and if the type of the property can be inferred from the initializer (or the getter return type), then the attribute type (PropertyType) is optional, as follows:

/ / var weight: Int?// reports an error, which needs to be initialized. The default getter and setter methods are hidden

Var height = 172 / / the type is inferred to be Int according to the initialization value, the attribute type does not need to be displayed, and the getter and setter methods are implemented by default

Unlike a mutable property declaration, a read-only property declaration starts with val rather than var, and the setter function is not allowed to be set because it is read-only.

Val type: Int?// type is Int and must be initialized. The getter method is implemented by default.

Val cat = 10max / type is Int, and the getter method is implemented by default

Init {/ / initialize the property, or you can initialize it in the constructor

Type = 0

}

The getter and setter functions of attributes in Kotlin can be omitted, and the system has a default implementation, as follows:

Class Person {

/ / when decorated with var, it must be initialized, even if there is getter (), but the value obtained is the value returned by getter ().

Var name: String? = "Android"

Get () = field / / default implementation, which can be omitted

Set (value) {/ / default implementation, which can be omitted

Field = value / / value is the parameter value of the setter () method, and field is the property itself

}

}

Where field represents the attribute itself, and the back-end variable, which will be discussed in more detail below, value is the parameter value of set (), and you can also change the name you like. Set (value) {field = value} means that the set () method assigns the set parameter value to the property field, and the above getetter () and setter () are the default implementations and can be omitted.

2.2 customization

We omitted the getter and setter methods from the above properties. We can define accessors for properties, and custom getetter () and setter () can make rules for method values according to their actual situation. For example, customize the get () and set () methods in Java.

(1) customization of getter () function of attributes modified by val

If you define a custom getter, the getter () method is called every time the property is accessed. Here is an example of a custom getter:

/ / when decorated with val, the default value can not be assigned when other values are specified with the getter () function attribute, but there can be no setter () function, equivalent val id: String = "0"

Val id: String

Get () = "0" / / define the get method for the attribute

If the type of the property can be inferred from the getter method, the type can be omitted:

Class Person {

/ / color returns a value based on the condition

Val color = 0

Get () = if (field > 0) 100 else-1 / / define the get method

/ / isEmpty attribute is used to determine whether color is equal to 0

Val isEmpty get () = this.color = = 0 / / Boolean type, which can be inferred from the getter method.

}

/ / call

Var person = Person ()

Log.e (TAG, "get () and set (): color = = ${person.color} | isEmpty = = ${person.isEmpty}")

By default, color defaults to 0Get (). The data of-1Magi isEmpty is false. The print data is as follows:

Get () and set (): color = =-1 | isEmpty = = false

one

(2) customization of getter () and setter () functions of attributes modified by var

Customize a setter that will be called each time an attribute is assigned, as follows:

Class Person {

Var hair: String = ""

Get () = field / / define the get method

Set (value) {/ / define the set method

Field = if (value.isNotEmpty ()) value else "" / / returns its value if it is not empty, otherwise returns ""

}

Var nose: String = ""

Get () = "Kotlin" / / the value is always Kotlin and will not change

Set (value) {

Field = if (value.isNotEmpty ()) value else "" / / returns its value if it is not empty, otherwise returns ""

}

}

Var person = Person ()

Person.hair = "Android"

Person.nose = "Android"

Log.e (TAG, "get () and set (): hair = = ${person.hair} | nose = = ${person.nose}")

The value of the getter () function in nose is fixed and will not be changed. The print data is as follows:

Get () and set (): hair = = Android | nose = = Kotlin

one

To sum up:

1. Properties decorated with val cannot have a setter () method

two。 Properties implement the getter () and setter () methods by default, which can be omitted if they are not overridden.

2.3 visibility

If you need to change the visibility of the accessor or annotate it, but do not need to change the default implementation, you can define the accessor without defining its body:

Class Person {

Val tea: Int = 0

/ / private set reported an error. Attributes modified by val cannot have setter.

Var soup: String = "Java"

/ / @ Inject set uses Inject annotations to implement setter ()

Var dish: String = "Android"

/ / private get error, cannot have the visibility of getter () accessor

Var meal = "Kotlin"

The private set / / setter accessor is privatized, and it has a default implementation of kotlin

}

Var person = Person ()

/ / person.meal = "HelloWord" error. Setter has been declared private and cannot be reassigned.

If the visibility of the property accessor is changed to private or the property is decorated directly with private, you can only manually provide a public function to change its property, similar to Bean.setXXX () in Java.

III. Backup fields and attributes

3.1 backup field (Backing Fields)

The backup field is a new definition compared to Java, and the field cannot be declared directly in the Kotlin class. However, when the attribute requires a backup field, Kotlin has a back-end variable mechanism (Backing Fields) that automatically provides it. You can use the backup field identifier field in the visitor to refer to this field, that is, the backup field in Kotlin is represented by field.

Why provide backup fields?

Class Person {

Var name: String = ""

Get () = "HelloWord" / / the value is always Kotlin and will not change

}

Var person = Person ()

Person.name = "HelloUniverse"

Log.e (TAG, "backup field: goose = = ${person.name}")

Note that we clearly assign the value through person.name= "HelloUniverse", but the default "HelloWord" value is still printed. The print data is as follows:

Backup field: name = = HelloUniverse

one

The above problem is obvious, because we define the getter () method of name in Person. Every time we read the value of name, get is executed, and get just returns "HelloWord", so can we just replace "HelloWord" with name? Let's transform it:

Class Person {

Var name: String = ""

/ / get () = "HelloWord"

Get () = name / / defines the get method for name, which returns name

}

Var person = Person ()

Person.name = "HelloUniverse"

Log.e (TAG, "backup field: name = = ${person.name}")

So what does the above code print after execution? "HelloUniverse"? The correct answer: no. The above is incorrect and causes infinite recursion at run time until the java.lang.StackOverflowError stack overflows. Why?

Because the get () method is called when we get the value of person.name, and the get () method accesses the name property (that is, return name), which in turn calls the get () method of the name property, which repeats until the stack overflows. The same is true of the set () method, which customizes the value of name:

Class Person {

Var name: String = ""

Set (value) {/ / defines the set method for name

Name = value// assigns a new value to name, namely value

}

}

Var person = Person ()

Person.name = "HelloUniverse"

Log.e (TAG, "backup field: name = = ${person.name}")

Similarly: the above code throws a stack overflow exception because name = value infinitely triggers the set () method of name.

So how do we modify the values of properties externally when we customize their get and set methods?

This is the function of the backup field. The above problem can be solved effectively through field. The code is as follows:

Var name: String? = "" / / Note: initializers assign backup fields directly.

Get () = field// returns field directly

Set (value) {

Field = value// assigns value directly to field

}

Var person = Person ()

Person.name = "HelloUniverse"

Log.e (TAG, "backup field: name = = ${person.name}")

The print data is as follows:

Backup field: name = = HelloUniverse

one

If the property uses at least one default implementation of the accessor, or if the custom accessor references the property through the field identifier, the fields supported by the property are generated. That is, the backup field field exists only if the default getter () or setter () is used and the display uses the field field. There is no backup field in the following code:

Val isEmpty: Boolean

Get () = this.color = = 0

The get () method is defined here, but it is not referenced through the fallback field field.

Note: the fallback field field can only be used for accessors of properties.

3.2 backup attributes (Backing Properties)

If you want to do something that is not suitable for backup fields, you can use backup properties to do so:

Private var _ table: Map? = null// backup attribute

Public val table: Map

Get () {

If (_ table = = null) {

_ table = HashMap () / / initialize

}

/ / return _ table if _ table is not empty, otherwise an exception is thrown

Return _ table?: throw AssertionError ("Set to null by another thread")

}

The _ table attribute is a private private and cannot be used directly, so a public fallback attribute table is provided to initialize the _ table attribute. This is the same way Java defines the bean property, because the get () and set () methods that access private properties are optimized by the compiler to access their actual fields directly without introducing the overhead of function calls.

Compile-time constants

The so-called compile-time constant is a constant that can determine the value at compile time.

4.1 the difference between compile time and run-time constant

There is also a runtime constant corresponding to the compilation constant, which can only be determined at run time, cannot be determined at compile time, and placed in the runtime constant pool. For run-time constants, the compiler can only determine that other code snippets cannot modify their assignments. For the difference between the two, look at the Java code:

Private static final String mark = "HelloWord"

Private static final String mark2 = String.valueOf ("HelloWord")

Two constants are defined: mark and mark2, so do you think they are different? Most people think it makes no difference, it's constant. But it's actually different. Let's take a look at their bytecodes:

Private final static Ljava/lang/String; mark = "HelloWord"

Private final static Ljava/lang/String; mark2

We found that the compiled mark assigns "HelloWord" directly, while mark2 does not. In fact, mark2 is assigned only when the class constructor is initialized, that is, at run time. This is the difference between compile-time constants (mark) and runtime constants (mark2)!

4.2 compile-time constant

In Kotlin, compile-time constants are decorated with const modifiers, which must meet the following requirements:

Must belong to the top-level Top-level, or a member of an object declaration or accompanying object

Initialization variables modified by basic data types or String types

There is no custom getter () method

Only those modified by val can be modified with const.

/ / Top-level top-level

Const val CONST_STR = "" / / correct, member of top-level level

/ / const val CONST_USER = User () / / error. Const can only modify basic types and values of type String. Point is an object.

Class Person {

/ / const val CONST_TYPE_A = 0 / / compilation error, there is no violation here can only be modified with val, but the properties here do not belong to the top-level level, nor do they belong to the

Object Instance {/ / the Instance here is modified with the object keyword, which means that Instance is a singleton. In fact, Kotlin has already built a singleton for us, so there is no need to write a singleton like Java.

/ / const var CONST_TYPE_B = 0 / / compilation error, variables modified by var cannot be modified by const

Const val CONST_TYPE_C = 0 / / correct, belongs to object class

}

}

These properties can also be used in comments:

Const val CONST_DEPRECATED: String = "This subsystem is deprecated"

@ Deprecated (CONST_DEPRECATED) fun foo () {

/ / TODO

}

The application scenarios of const are basically included here. But some people may ask, why would Kotlin provide a val modifier when it provides a const modifier? In theory, val can already represent constants, so why provide const?

4.3 the difference between const and val

The following code belongs to the Kotlin code, and the top-level constant is located in the kotlin file Person.kt and belongs to the top-level level:

Const val NAME = "HelloWord"

Val age = 20

Class Person {

}

The following code is Java code, which is used to test and create a class in the main function data:

Public class ConstCompare {

Public static void main (String [] args) {

/ / pay attention to the following two calling methods

System.out.println (PersonKt.NAME); / / Note here: kotlin files generate java class files with kotlin filename + Kt by default.

System.out.println (PersonKt.getAge ())

/ / an error was reported in the compilation. The calling method of PersonKt.age is incorrect.

/ / System.out.println (PersonKt.age)

}

}

The above code proves the difference between const-decorated fields and val-decorated fields: fields decorated with const can be called directly with the class name + field name, similar to Java's private static final decoration, while val-decorated fields can only be called in the form of the get method.

So the purpose of const is only to identify public static fields?

No, in fact, it is the const decorated field NAME that becomes a public field (that is, public). This is the implementation mechanism of Kotlin, but it is not the static variable generated because of const. Let's look at the bytecode of the Person class:

/ / PersonKt is the Java class file generated by Kotlin

Public final class com/suming/kotlindemo/blog/PersonKt {

/ / pay attention to the bytecode of the following two fields NAME and age

/ / access flags 0x19

/ / Kotlin actually generates public final static-decorated Java fields for NAME

Public final static Ljava/lang/String; NAME = "HelloWord"

@ Lorg/jetbrains/annotations/NotNull; () / / invisible

/ / access flags 0x1A

/ / Kotlin actually generates private final static-decorated Java fields for age

Private final static I age = 20

/ / Note: here is the method to generate getAge ()

/ / access flags 0x19

Public final static getAge () I

L0

LINENUMBER 14 L0

GETSTATIC com/suming/kotlindemo/blog/PersonKt.age: I

IRETURN

L1

MAXSTACK = 1

MAXLOCALS = 0

/ / access flags 0x8

Static Construction method of static () V / / Kotlin Generation

L0

LINENUMBER 14 L0

BIPUSH 20

PUTSTATIC com/suming/kotlindemo/blog/PersonKt.age: I

RETURN

MAXSTACK = 1

MAXLOCALS = 0

/ / compiled from: Person.kt

}

As can be seen from the bytecode above:

1.Kotlin generates final static identities for both NAME and age fields, but NAME is public and age is private, so you can access NAME directly through the class name rather than age through the class name

2.Kotlin generates a public final static-decorated getAge () method for age, so you can access age through getAge ().

In summary, the summary of const val and val is as follows:

Both const val and val generate static final-decorated fields corresponding to Java, while const val is decorated with public and val with private. At the same time, the compiler generates a get method for the val field for external access.

Note: check the bytecode through Android studio > Tools > Kotlin > Show Kotlin ByteCode.

Fifth, delayed initialization of attributes and variables

   typically, properties declared as non-empty types must be initialized (in the constructor). However, this is usually very inconvenient. For example, in unit testing, properties are generally initialized in the setUp method; in the dependency injection framework, only defined fields are used and do not need to be initialized immediately.

In this case, you cannot provide a non-empty initializer in the constructor, but you want to avoid null checking when referencing properties in the body of the class. Kotlin has designed a delayed initialization mechanism for this scenario. You can use the lateinit modifier to mark attributes and delay initialization, that is, it does not have to be initialized immediately or in the constructor, but can be initialized later in a suitable actual initialization. Variables modified with the lateinit keyword need to meet the following points:

Variables of type val cannot be modified

Cannot be declared in a nullable variable, that is, a type followed by a?, such as String?

After modification, the variable must be initialized before use, otherwise a UninitializedPropertyAccessException exception will be thrown

Basic data type variables cannot be modified, such as data types such as Int,Float,Double. String types are allowed.

No additional null judgment processing is required. If the property has not been initialized, a null pointer exception will be thrown during access.

You can only modify properties that are located in class body, not properties that are located in constructors.

Public class MyTest {

Lateinit var subject: TestSubject / / non-empty type

@ SetUp fun setup () {

Subject = TestSubject () / / initialize TestSubject

}

@ Test fun test () {

Subject.method () / / Constructor call

}

}

The lateinit modifier can be used for var properties declared within the body of the class (not in the main constructor and only if the property does not have custom getter and setter). Since Kotlin 1.2, it can be used for top-level properties and local variables. The type of the property or variable must be non-empty and cannot be the original type. Accessing a lateinit-decorated property before it is initialized throws an exception that clearly identifies the property being accessed and the fact that it is not initialized.

Since Kotlin 1.2, you can check whether lateinit-decorated variables have been initialized, and use the this:: variable name .isInitialized in attribute references. This can be saved:

Lateinit var person: Person / / lateinit indicates delayed initialization and must be non-empty

Fun method () {

Person = Person ()

If (this::person.isInitialized) {/ / returns true if it has been assigned, false otherwise

/ / TODO

}

Log.e (TAG, "delayed initialization: person.isInitialized = = ${:: person.isInitialized}")

}

The print data is as follows:

Delayed initialization: person.isInitialized = = true

one

Note: this check is available only for properties that are lexically accessible, such as properties declared on the same type or external type, or at the top level of the same file. However, it cannot be used for inline functions to avoid binary compatibility issues.

Thank you for reading this article carefully. I hope the article "sample Analysis of attributes and Fields in Kotlin" shared by the editor will be helpful to you. At the same time, I also hope that you will support and pay attention to the industry information channel. More related knowledge is waiting for you to learn!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Internet Technology

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report