Class and inheritance
class
Define a class
class Person
Copy the code
The constructor
- Constructors in Kotlin have primary constructors and secondary constructors
Primary constructors take precedence over secondary constructors
class Person(val name: String) {
val age: Int
init {
if (this.name == "zhazha") {
this.name = "Z" + this.name
}
this.age = 20
}
constructor(name: String, age: Int) : this(name) {
// This constructor does not allow the initialization of the val age: Int field, which can only be initialized in the init block
// this.age = age}}Copy the code
In the example above, the val age: Int and init code blocks are executed in order of precedence over the secondary constructor
Note 1:
Class Person(val name: String) and class Person(name: String) are both attributes and function parameters
Note 2: The constructor keyword is required if the constructor has annotations or visibility modifiers and those modifiers precede it
class Person private/ *@Inject* /constructor(name: String) {
val name: String = name.uppercase()
}
Copy the code
Note 3: The secondary constructor always delegates initialization to the primary constructor, which then executes the code for the init code block, and finally executes the secondary constructor
Note 4:
Init blocks end up inside the body of the main constructor
class Person(var name: String) {
init {
if (this.name == "haha") {
this.name = "zhazha"}}}Copy the code
Will become
public final class Person {
private final String name;
public Person(a) {
this.name = name;
if (Intrinsics.areEqual(this.name, "haha")) {
this.name = "zhazha"}}// get/set ...
}
Copy the code
inheritance
open class Base(p: Int)
class Derived(p: Int) : Base(p)
Copy the code
(1) To inherit, the subclass needs to inherit the parent class, the subclass is responsible for the parent class field initialization
class Derived(p: Int) : Base(p)
(2) If the subclass does not have a primary constructor, this time the constructor needs to manually call the parent constructor using super.
open class Base(val p: Int) {
// Name can be initialized in a block of init code instead of "". It will eventually be merged into the main constructor
var name: String = ""
constructor(p: Int, name: String): this(p) {
this.name = name
}
}
class Derived : Base {
// If the subclass does not explicitly write out the primary constructor, the secondary constructor needs to use super to actively call the parent constructor (primary or secondary).
constructor(p: Int) : super(p) {}
constructor(p: Int, name: String) : super(p, name) {}
}
Copy the code
If the class needs to be inherited, this should be explicitly written out as the Open class name, otherwise the default is public Final
Override method (override method)
open class Shape {
/* protected */ open fun draw(a) {}
fun fill(a){}}class Circle() : Shape() {
override fun draw(a){}}Copy the code
(1) If the method of the parent class needs to be overridden, it needs to actively write the open keyword
(2) The override keyword needs to be displayed after the subclass overrides the method of the parent class
(3) A method ina class is public final by default. If you want to override it, you must explicitly write open
(4) If the class is final, then the open keyword function is invalid
What’s the use of an open method when the parent class is not allowed to inherit? So IDA will just report an error
(5) If a subclass of Open overrides a method that does not need to be overridden again by another class, then final can be explicitly written
open class Shape {
open fun draw(a){}}open class Circle : Shape() {
// If class Circle is final open Circle, the function is also final and can be written without display
final override fun draw(a){}}Copy the code
Covering properties
A subclass has the same attribute name as its parent
open class Shape {
open val vertexCount: Int = 0
}
class Rectangle : Shape() {
override var vertexCount: Int = 1
}
Copy the code
(1) open val vertexCount: Int = 0
No, final and open use the opposite functionality for classes, but not for fields
On fields: (Field visibility is always private)
When val is used, fields are private final by default, and get/set only generates GET
val ==> private final ==> final get
Var is private and generates get/set functions
var ==> private ==> final get/set
All the functions generated above are final (get/set are all final)
If you add open to a field, it will affect the get/set function. If you add open to a field, the final of get/set will disappear
(2) You can also override a val attribute with a var attribute, but not vice versa. This is allowed because a val attribute essentially declares a get method, while overwriting it as var simply declares an additional set method in a subclass
The subclass wants the vertexCount field to have only the GET method and be final, but the parent class already has a set method. By default, the subclass inherits the parent class’s public Final set method
Class initialization order
Explore how to create subclass objects in the initialization order
open class Base(val name: String) {
init {
println(2. Superclass init code block)}open val size: Int = this.name.length.also { println("3. The parent constructor performs size object initialization")}}class Derived(name: String, val lastName: String) :
Base(name.capitalize().also { println("1. The initialization code block of the Derived constructor performs initialization.") {})init {
println("4.Derived init code block execution")}override val size: Int =
(super.size + lastName.length).also { println("5. Initialize the size field Derived")}}fun main(args: Array<String>) {
val derived = Derived("zhazha"."xixi")}Copy the code
The specific order is:
1The initializer block of the.Derived constructor performs the initialization2. Superclass init code block3The superclass constructor performs size object initialization4The.Derived init code block is executed5Initialize the size field DerivedCopy the code
Something that calls the parent class
open class Rectangle {
open fun draw(a) { println("Drawing a rectangle")}val borderColor: String get() = "black"
}
class FillRectangle : Rectangle() {
override fun draw(a) {
// super calls the superclass function
super.draw()
println("Filling the rectangle")}// Call the properties of the parent class, where the getBorderColor() function is called
val fillColor: String get() = this.borderColor
}
Copy the code
(1) Super is generally used to call attributes or methods of the parent class
open class Rectangle {
open fun draw(a) {
println("Drawing a rectangle")}val borderColor: String get() = "black"
}
class FilledRectangle : Rectangle() {
override fun draw(a) {
// super calls the superclass function
super.draw()
println("Filling the rectangle")}// Call the properties of the parent class, where the getBorderColor() function is called
val fillColor: String get() = this.borderColor
inner class Filler {
fun fill(a) {
println("Filling ")}fun draw(a) {
println("Filler draw...")}fun drawFill(a) {
// idea intelligent prompt is not very good for this, it needs to be written by the user, no prompt
// Only the inner class can access the outer class's methods, and the parent of the FilledRectangle class is called draw
super@FilledRectangle.draw()
fill()
println("fillColor = $fillColor")
// Call Filler's draw
draw()
// Call FilledRectangle's draw
this@FilledRectangle.draw()
println("class rectangle color: ${[email protected]}")}}}fun main(args: Array<String>) {
val rectangle = FilledRectangle()
rectangle.draw()
rectangle.Filler().drawFill()
}
Copy the code
1) super @ AAA class. BB method (), which calls the BB method ② this@AAA class of the parent class of AAA. BB method (), which calls the BB method of class AAA
Covers the rules
open class Rectangle {
open fun draw(a) {
println("rectangle draw...")}}interface Polygon {
fun draw(a)
}
class Square() : Rectangle(), Polygon {
// Although both parent classes have draw methods, one of them is an interface, and the other already has a function body, so it calls the implemented method directly
}
fun main(args: Array<String>) {
val square = Square()
square.draw()
}
Copy the code
If there are two classes, you need to override the desired method directly
interface Rectangle {
fun draw(a) {
println("Rectangle draw...")}}interface Polygon {
fun draw(a) {
println("Polygon draw...")}}class Square : Rectangle.Polygon {
// Class 'Square' must override public open fun draw(): Unit defined in relation04.cover02.Rectangle because it inherits multiple interface methods of it
override fun draw(a) = println("Square draw...")}fun main(args: Array<String>) {
val square = Square()
square.draw()
}
Copy the code
The default functions of both interfaces have the same name, and subclasses need to manually override this method to define their own draw function
interface Rectangle {
fun draw(a) = println("Rectangle draw...")}interface Polygon {
fun draw(a) = println("Polygon draw...")}class Square : Rectangle.Polygon {
override fun draw(a) {
super<Rectangle>.draw()
super<Polygon>.draw()
println("Square draw...")}}fun main(args: Array<String>) {
val square = Square()
square.draw()
}
Copy the code
An abstract class
abstract class Polygon {
abstract fun draw(a)
}
class Rectangle : Polygon() {
override fun draw(a) {
println("Rectangle draw...")}}Copy the code
You can also override non-abstract Open member functions with abstract member functions
open class Polygon {
open fun draw(a) {
println("Polygon draw...")}}abstract class Rectangle : Polygon() {
// The subclass overwrites the parent's open method as abstract
abstract override fun draw(a)
}
Copy the code
attribute
Declaration attributes
class Address {
var name: String =
"Holmes, Sherlock"
var street: String = "Baker"
var city: String = "London"
var state: String? = null
var zip: String = "123456"
}
Copy the code
In my opinion, the fields in the Kotlin class are not simply fields, but fields + GET /set methods, which are properties
(1) var and val. Var defines a readable and writable property, and val defines a read-only property
This. Name is equivalent to calling getName, this. Name = “hey hey “, which is equivalent to calling setName(” hey hey “).
var allByDefault: Int? // Error: requires an explicit initializer with implied default getters and setters
var initialized = 1 Type Int, default getter, and setter
val simple: Int? // Type Int, default getter, must be initialized in constructor
val inferredType = 1 // Type Int, default getter
Copy the code
(2) Custom accessors
class Address {
// It can be initialized in the init initialization field or directly in name
// val name: String = 0
val name: String
get() = field
var age: Int = 0
get() = field
set(value) {
field = value
}
init {
this.name = ""
}
var isEmpty: Boolean = age == 0
}
var setterVisibility: String = "abc"
private set This setter is private and has a default implementation
var setterWithAnnotation: Any? = null
@Inject set // Annotate this setter with Inject
Copy the code
Behind the field
Infinite recursion problem:
class Teacher {
var name: String
get() = this.name
set(value) {
this.name = value
}
}
Copy the code
(1) This.name will be resolved to this.getName(), and the location where this method is called is in getName, so it is infinite recursion
(2) this.name = value will be resolved to this.setName(value), and the call location is in the setName function, so it is still infinite recursion
This is where we need the field behind the scene field
class Teacher {
var name: String = ""
get() = field
set(value) {
field = value
}
}
Copy the code
Behind the properties
private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
get() {
if(_table == null) {
_table = HashMap()
}
return_table ? :throw AssertionError("Set to null by another thread")}Copy the code
constant
The const tag val property will be parsed as a constant
(1) A constant cannot be placed inside a class. It can only belong to the top layer or objects
const val INT_MIN = Int.MIN_VALUE
class Test {
companion object {
const val INT_MAX = Int.MAX_VALUE
}
}
Copy the code
Lazy initialization of properties and variables
Most of the time fields do not need to be initialized immediately, such as in the Spring framework’s dependency injection. The initialization of properties needs to be actively initialized by the Spring container, so the user does not need to initialize them before running
lateinit var subject1: LateInitTest
open class LateInitTest {
lateinit var subject: LateInitTest
// This field can only be init inside the field or in the same area as the top-level property
fun isInitSubject(a): Boolean = this::subject.isInitialized
}
fun main(args: Array<String>) {
val initTest = LateInitTest()
println(initTest.isInitSubject())
println(::subject1.isInitialized)
}
Copy the code
(1) Lateinit cannot modify basic data types. (2) LateInit cannot initialize an attribute of val. (3) Lateinit’s isInitialized function can only be used in the same scope. The other is in the scope of class LateInitTest
interface
Kotlin’s interface can contain both declarations and implementations of abstract methods. Unlike abstract classes, interfaces cannot hold state. It can have attributes but must be declared abstract or provide accessor implementations
interface MyInterface {
var name: String
val age:Int
fun bar(a)
fun foo(a) {
println(this::javaClass)
}
}
Copy the code
Interfaces can have interfaces and they can have default methods and they can have properties
interface Named {
val name: String
interface Name {
val names: String
}
}
interface Person : Named {
val firstName: String
val lastName: String
override val name: String
get() = "$firstName $lastName"
}
class NameClass(override val names: String) : Named.Name {
}
data class Employee(override val firstName: String, override val lastName: String) : Person {
val position: Pair<Double.Double> = Pair(0.0.0.0)}fun main(args: Array<String>) {
val employee = Employee("zzz"."ddd")
println(employee.name)
}
Copy the code
The implementation of the default method in the interface is more complicated. This involves kotlin’s early object is JDK1.6, when the class does not allow default methods, so kotlin’s implementation is more interesting, below is the Java source code
public void foo(a);
@Metadata(mv={1, 5, 1}, k=3, xi=48)
public static final class DefaultImpls {
public static void foo(@NotNull MyInterface this_) {
Intrinsics.checkNotNullParameter((Object)this_, (String)"this");
PropertyReference0Impl propertyReference0Impl = new PropertyReference0Impl(this_){
@Nullable
public Object get(a) {
return this.receiver.getClass(); }};boolean bl = false; System.out.println((Object)propertyReference0Impl); }}Copy the code
The interface does not allow scope (fields) for recording data, so fields defined in the interface are handled by Kotlin as set/ GET methods
public interface MyInterface {
@NotNull
public String getName(a);
public void setName(@NotNull String var1);
public int getAge(a);
}
Copy the code
Interface implementation
Interfaces, like interfaces in Java, can be multiple interfaces, and entity classes can implement multiple interfaces
interface A {}
interface B {}
interface C : A.B {}
class D : A.B {}
Copy the code
If interface A and interface B use the same method, it is ok if fun Hello(): Unit is found by D, there is only one implementation
interface A {
fun hello(a)
}
interface B {
fun hello(a)
}
class D : A.B {
override fun hello(a){}}Copy the code
Attributes in interfaces
Attributes in interfaces are either abstract or provide accessors (get/set)
interface MyInterface {
var name: String
val age: Int
get() = 10
}
class MyClass(override var name: String) : MyInterface {
}
fun main(args: Array<String>) {
val myClass = MyClass("zhazha")
println(myClass.age)
}
Copy the code
When a subclass implements an interface, it does not need to override the interface property (age property above) if the interface property does not provide accessors. If the interface property does not provide accessors, it needs to override the property (such as name property).
Interface inheritance
interface Named { val name: String } interface Person : Named { val firstName: String val lastName: String override val name: String get() = "$firstName $lastName" } data class Employee(override val firstName: Person {val position: Pair<Double, Double> = Pair(0.0, 0.0)}Copy the code
Functional interface
The interface of a single abstract method is called a functional interface or SAM interface
fun interface KRunnable {
fun invoke(a)
}
Copy the code
Functional interfaces in Kotlin seem to be similar to Functional interfaces in Java
Methods that call a functional interface
fun interface KRunnable {
fun invoke(a)
}
fun interface IntPredicate {
fun accept(i: Int): Boolean
}
fun isInt(i: Int, funcType: IntPredicate): Boolean = funcType.accept(i)
fun main(args: Array<String>) {
val a = 19
println(isInt(a){
it is Int})}Copy the code
Functional interfaces are essentially interfaces. In Java, functional types are not allowed, so interfaces that have only one abstract method can be used as types to receive subclasses (mostly anonymous) that implement the interface. So a functional interface is a user-defined type (such as an Int String), and we pass in objects defined by that type. That is, there is only one functional interface (type only one), but there can be many, many objects defined by type
Visibility modifier
Classes, objects, interfaces, constructors, methods, properties, and their setters can all have visibility modifiers (getters always have the same visibility as properties)
There are four visibility modifiers in Kotlin: private, protected, internal, and public. If no specified visibility modifier is displayed, the default is public
package
- If no visibility modifier is specified, the default top-level declaration is public
- If declared private, it can only be used within the file
- If declared internal, it will be used anywhere in the module (as in Java’s default)
- This modifier is not supported in the protected package
Classes and interfaces
- Private: indicates that the class is visible internally. All members, including its internal members, can be used only within the class
- Protected: Indicates that members of the class are visible under subclasses of the entire family of classes
- Internal: visible throughout the module
- Public: visible anywhere
As with Java, external classes cannot access private inner classes
If a protected member is overridden and no visibility modifier is specified, the default remains protected
open class Outer {
private val a = 1
protected open val b = 2
internal val c = 3
val d = 4 / / public by default
protected class Nested {
public val e: Int = 5}}class Subclass : Outer() {
// A is not visible
// b, C, d
// Nested and e are visible
override val b = 5 / / "b" is protected
}
class Unrelated(o: Outer) {
// O.a, O.B are not visible
// o.c and O.D visible (same module)
// Nested. Nested:: E was also invisible
}
Copy the code
The constructor
To specify the visibility of a class’s main constructor, use the following syntax (note that you need to add an explicit constructor keyword) :
Class C private constructor(a: Int) {... }
The constructor here is private. By default, all constructors are public, which essentially equals that the class is visible wherever it is visible
A local variable
Local variables, functions, and classes do not support visibility modifiers
The module
The internal visibility modifier means that the member is visible only within the same module, and a module is a bunch of Kotlin files compiled together, for example:
- An IDEA module
- A Maven project
- A Gradle source-set (except that the test source-set has access to the internal declaration of main)
- At a time
<kotlinc>
The Ant task executes a compiled set of files
extension
Extensions include extension functions and extension properties. Classes in third-party libraries are usually not allowed to change, but you can use extension functions to add new functions to third-party libraries, and you can use extension properties to add new properties to third-party libraries
Extension function
We talked about extension functions and attributes in the previous section, but let’s do it again just to impress you
Defines a method that extends a function
We add a method to the MutableList
that swaps elements
(1) Define a function
fun swap(index1: Int, index2: Int) {}
Copy the code
(2) Add extensions
Add the operation to which the extension function belongs
fun MutableList<Int>.swap(index1: Int, index2: Int){}Copy the code
(3) We can now use the this pointer in the function body
fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp: Int = this[index1]
this[index1] = this[index2]
this[index2] = tmp
}
Copy the code
(4) How to use it?
fun main(args: Array<String>) {
val list = MutableListOf(6.2.3.4.5.1)
list.swap(0, list.size - 1)
list.forEach { println(it) }
}
Copy the code
Add generics for extension functions:
fun <T> MutableList<T>.swap(index1: Int, index2: Int) {
val tmp = this[index1]
this[index1] = this[index2]
this[index2] = tmp
}
Copy the code
Extension functions are static
Extension functions don’t really add functions to a class, just to use expression points on objects of that class. In a way
Extension functions are static methods in Java source code
The use of extension functions is also a static process, which is not based on the [1] virtual function of the receiver type (VPTR and vtable in c++). It is an expression syntax. It does not dynamically look up the specific subclass type of the virtual function table at runtime based on the type of the receiver. Instead, it is the type given by the expression, so it is static
open class Shape {}
class Rectangle: Shape() {}
fun Shape.getName(a): String = "Shape"
fun Rectangle.getName(a): String = "Rectangle"
fun printName(s: Shape) {
println(s.getName())
}
fun main(args: Array<String>) {
val r = Rectangle()
printName(r) // Prints Shape
}
Copy the code
As you can see from the code above, the r object is Rectangle and the printName argument is Shape. In polymorphic fashion, it should print the subclass’s “Rectangle” instead of “Shape”. So to prove that the extension function is not dynamically compiled, just print “Shape” according to the Shape displayed in the expression, no matter what subclass you pass in
[1] virtual functions, in c++, are implemented in the form of virtual Pointers and virtual tables. The virtual function pointer points to the virtual table, and the virtual function pointer is stored in the first few bytes of the class object (4 bytes or 8 bytes). The virtual function table is generated whenever a class has virtual functions (functions marked by the virtual keyword include pure virtual functions), and the virtual function pointer is initialized twice during the process of new an object. The first time the constructor of the parent class is called, the virtual pointer points to the parent class’s virtual table address. The second call to the subclass constructor points to the subclass’s virtual table address, and the entire initialization process is dynamic, also known as dynamic binding (or dynamic association)
Member functions and extension functions
Member functions take precedence over extension functions
When a function in a class and an extension function have the same function signature, the Kotlin compiler preferentially selects the member function. If the extension function and the extension function have different function signatures, it is not affected
class Example {
fun printFunctionType(a) = println("class Method")}fun Example.printFunctionType(a) = println("extension method")
fun main(args: Array<String>) {
val example = Example()
example.printFunctionType() // class Method
}
Copy the code
Void receiver
You can see from the previous code:
funAny? .toString(a): String = if (this= =null) "null" else toString()
// The above code can be simplified, it seems that I can not kotlin ability, think of can use? If = if = if = if = if = if = if = if = if
fun Any?toString(a): String = this? .toString() ? :"null"
Copy the code
See?? Bright spot in Any? It is similar to Object in Java. The parent class of all objects
The nice thing about this is that it adds an extension function called toString to all of the classes, and it adds the part we want, null.tostring () doesn’t give an error, it prints “null” strings, So no matter what kind of nullable object calls toString() will always succeed
Extended attributes
The extension attribute and extension function are basically the same, but since the extension has no corresponding member field, the background object (field) for the extension attribute is invalid. Therefore, the extension attribute cannot be initialized. It should have no corresponding member field (if any, only this pointer).
Extended properties add only get/set functions
How do I define an extended attribute?
First of all, it’s a property, defined by property
(1) Define an attribute
val lastIndex: Int
Copy the code
(2) Add extensions
val List<Int>.lastIndex: Int
Copy the code
Write the get/set (3)
val List<Int>.lastIndex: Int
get() = size - 1
Copy the code
It’s val so there’s only a get function, not a set function
val List<Int>.lastIndex: Int
get() = size - 1
fun main(args: Array<String>) {
val list = listOf(1.2.3.4.5)
println(list.lastIndex)
println(list[list.lastIndex])
}
Copy the code
Java source code:
public final class ExtensionFieldDemo01Kt {
public static final int getLastIndex(@NotNull List<Integer> $this$lastIndex) {
Intrinsics.checkNotNullParameter($this$lastIndex, (String)"<this>");
return $this$lastIndex.size() - 1;
}
public static final void main(@NotNull String[] args) {
Intrinsics.checkNotNullParameter((Object)args, (String)"args");
Object[] arrobject = new Integer[]{1.2.3.4.5};
List list = CollectionsKt.listOf((Object[])arrobject);
int n = ExtensionFieldDemo01Kt.getLastIndex(list);
boolean bl = false;
System.out.println(n);
n = ((Number)list.get(ExtensionFieldDemo01Kt.getLastIndex(list))).intValue();
bl = false; System.out.println(n); }}Copy the code
Extensions of associated objects
class MyClass {
companion object{}}fun MyClass.Companion.printCompanion(a) = println("companion")
val MyClass.Companion.index: Int
get() = 10
fun main(args: Array<String>) {
MyClass.Companion.printCompanion()
}
Copy the code
Extended scope
Extensions are also usually top-level functions or attributes
Extensions are declared as members
open class Host(private val hostName: String) {
fun printHostname(a) = print(hostName)
}
class Connection(private val host: Host, private val port: Int) {
private fun printPort(a) = print(port)
private fun Host.printConnectionString(a) {
printHostname()
print(":")
printPort()
}
fun connect(a) {
host.printConnectionString()
}
}
fun main(args: Array<String>) {
Connection(Host("kotlin.in"), 443).connect()
}
Copy the code
In the above code, there are distribution receivers and extension receivers, respectively
Distribution receiver: The class in which the extension declaration resides is called the distribution receiver
Extension receiver: An instance of the type of receiver on which the extension method call is made, called extension receiver
If the distribution receiver and the extension receiver have the same function name, the extension receiver’s function is preferred by default
class Connection {
fun Host.getConnectionString(a) {
toString() / / call the Host. The toString ()
this@Connection.toString() / / call Connection. The toString ()}}Copy the code
The distribution receiver is dynamic and polymorphic, but the extension receiver is static. Look at this code:
open class Base {}
class Derived : Base() {}
open class BaseCaller {
open fun Base.printFunctionInfo(a) = println("Base extension function in BaseCaller")
open fun Derived.printFunctionInfo(a) = println("Base extension function in BaseCaller")
fun call(b: Base) {
b.printFunctionInfo() // Call the extension function}}class DerivedCaller : BaseCaller() {
override fun Base.printFunctionInfo(a) = println("Base extension function in DerivedCaller")
override fun Derived.printFunctionInfo(a) =
println("Derived extension function in DerivedCaller")}fun main(args: Array<String>) {
val baseCaller:BaseCaller = BaseCaller()
baseCaller.call(Base()) // Base extension function in BaseCaller
val derivedCaller: BaseCaller = DerivedCaller()
derivedCaller.call(Base()) // Base extension function in DerivedCaller
derivedCaller.call(Derived()) // Base extension function in DerivedCaller
}
Copy the code
The visibility of the extension is the same as that of other entities declared in the same scope. For example, an extension declared at the top of a file can access other private top-level declarations in the same file; If the extension is declared outside its recipient type, the extension cannot access the recipient’s private members.