Chapter IV (classes, objects, and interfaces)

Kotlin classes and interfaces with Java classes and interfaces still have a little different. For example: interface can contain property declaration. Unlike Java, Kotlin of default is declared final and public the. In addition, the default class is not nested inner classes: they do not contain an implicit reference to its outer class.

Kotlin Interface

8 is similar to the interface Kotlin Java: they may comprise non-abstract definitions and methods implemented abstract methods. The difference is that with Java 8, you need Java 8 marked default keywords on such an implementation, and Kotlin no special comment: only need to provide a method body. Example:

interface Clickable {
    fun click()
    fun showOff() { //带默认方法的实现
        println("Clickable!")
    }
}

The above is the basic definition of the way the interface, then our understanding of how to implement the interface? Kotlin use a colon after the class name instead of in Java extends and implements keywords. Like Java, a class can implement any number of interfaces, but can only inherit a class. And the Java @Override annotations similar, Kotlin using override modifier is rewritten to label the parent class or method interface properties. Unlike Java is used in Kotlin override modifier is mandatory. 

open class TextView : Clickable {

    override fun click() {
        // 此方法必须实现
    }

//    override fun showOff() { //接口有默认实现,这里可以不用再实现此方法
//        super.showOff()
//    }

}

class Button : TextView() {
    // 需要注意的是,Kotlin中继承与实现接口都是用的冒号,区别在于继承类时,
    //如果父类有主构造方法,则需要在类名后添加括号及主构造函数中的参数,如果没有主构造方法,
    //则必须加上一个默认的空括号来表示。
    //如上面的TextView(),而继承接口只需要直接写接口名称即可。关于主构造函数下面有说
}

So if we have an interface Focusable, there is one and the same Clickable interface showOff method on this interface, when TextView inherit both interfaces when in the end how to achieve these two methods?

open class TextView : Clickable,Focusable {

  /**
  * 如果同样的继承成员有不止一个实现,那么必须最少提供一个显示实现
  * 即super<Clickable>.showOff()
        super<Focusable>.showOff()两行中最少要调用其中一行
  */
    override fun showOff() {
      // 通过下面代码显示的实现两个showOff方法
        super<Clickable>.showOff()
        super<Focusable>.showOff()
    }
    
    override fun click() {
        // 此方法必须实现
    }
}

Modifiers

open

Java, allows you to create a subclass of class and override any method, unless explicitly use the final keyword tagging. In Kotlin, the default is final , that is, the default is not inherited or overridden. If you want to allow to create a subclass of a class, you need to use open modifiers to identify the class. Further, a need for each property or method can be overridden to add open modifiers.

Note: If you override a base class or interface members rewrote the members of the same default is open. If you want to change this behavior, stop your subclass of the class rewrite your implementation, you can explicitly labeled as a member of the rewrite final .

open class RichButton:Clickable{ //这个类是open的,其他类可以继承它
    override fun click() {//这个函数重写了一个open函数并且它本身也是oepn的
        
    }
    fun disable(){} //这个函数是final的:不能在子类中重写它
    
    open fun animate(){} //这个函数是open的,可以在子类中重写它
}

abstract

In Kotlin, the same as Java, a class can be declared as abstract of this class can not be instantiated. An abstract class typically contains some did not materialize and must be overridden in a subclass of abstract members. Abstract members is always open in , there is no need to explicitly use the open qualifier.

abstract class Animated{ //这个类是抽象的:不能创建它的实例
    abstract fun animate()//这个函数是抽象的:他没有实现,必须被子类重写

    open fun stopAnimating(){ //抽象类中的非抽象函数并不是默认open的,但是可以标注为open的

    }

    fun animateTwice(){ // 不是open的,不能被重写

    }
}

Access modifiers meaning of class

Modifiers Relevant members annotation
final It can not be rewritten Class members default
open It can be rewritten To be clear show
abstract It must be rewritten Use only in an abstract class; abstract members can not have realized
override Override the parent class or member interface If no final show, is open override the default of a member

Visibility modifier (public, etc.)

Similarly visibility modifiers in the Java Kotlin in. Also you can use public, protected and private modifiers. But the default visibility is not the same: If you omit the qualifier, the default is the public declaration of.
Java in the default visibility - private package, not used in Kotlin in. Kotlin only the packet as a way to organize the code used in the namespace, and not as its visibility control.
Alternatively, Kotlin provides a new modifier, internal indicating "visible only inside the module." A module is a group of Kotlin files compiled together. This may be a Intellij IDEA module, an Eclipse project, a project Gradle or Maven or Ant task calls using a set of compiled files.

Kotlin visibility modifiers

Modifiers Class Members Top-level declarations
pulbic (default) All visible place All visible place
internal Modules can be seen Modules can be seen
protected Subclass visible Not protected modifier
private Visible class File visible

Example: The following functions of each row giveSpeech attempt to violate visibility rules. Error occurs at compile time.

 internal open class TalkativeButton:Focusable{
        private fun yell() = println("Hey!")
        protected  fun whisper() = println("talk")
    }
    
 fun TalkativeButton.giveSpeech(){ //错误:"public"成员暴露了其"internal"接收者类型TalkativeButton
      yell() //错误:不能访问"yell()":它在"TalkativeButton"中是"private"的
     whisper() //错误:不能访问"whisper()":它在"TalkativeButton"中是"protected"的
  }

To solve the problem in the example above, both the giveSpeech changed internal function, you can also change the type of public, of course, if you want to access the "yell and whisper method, you need to read these two methods public or internal of.

Internal nested classes and classes

Like Java, you can declare a class in another class in Kotlin in. The difference is Kotlin nested class can not access the instance of the outer class, unless you make a special request.
In Kotlin it has not been shown with the same nested class modifiers in Java static nested classes. Turn it into an internal class to hold a reference to the outer class, then you need to use inner modifiers. Examples of external reference classes in Kotlin Java syntax also different. You need to use this @ OuterClass to visit Outerclass class from InnerClass.

And inner nested classes in the Java class corresponds with the Kotlin

A class declared in another class B, In Java In the Kotlin
Nested class (class is not stored in the external reference) static class A class A
Internal class (class external memory references) class A inner class A

sealed sealed class: Define restricted inheritance structure

When using the structure to execute when the expression, Kotlin compiler will enforce check the default option. For example: Suppose Expr parent class has two subclasses: representing numbers Num, and represents the sum of two expressions two SUm. Handle all possible subclasses certainly very convenient when the expression, but must provide an else branch to deal with the absence of any other branch can match:

interface Expr

class Num(val value:Int):Expr
class Sum(val left:Expr,val right:Expr):Expr

fun eval(e:Expr):Int = when(e){
    is Num -> e.value
    is Sum -> eval(e.right)+ eval(e.left)
    else -> throw IllegalArgumentException("Unknown expression")
}

In the above example, else because the conditions can not return a meaningful value, directly it throws an exception. If you add a new sub-class, the compiler can not find a place to change. If you forget to add a new branch, it will select the default else branch, which could lead to potential bug.
Kotlin provides a solution to this problem: sealed class. Add a parent class sealed modifier, set strict limits on sub-classes may be created. All direct subclasses of the parent class must be nested.

sealed class Expr{
    class Num(val value:Int):Expr()
    class Sum(val left:Expr,val right:Expr):Expr()
}

fun eval(e:Expr):Int = when(e){
    is Expr.Num -> e.value
    is Expr.Sum -> eval(e.right)+ eval(e.left)
}

If you deal with a subclass of class in all sealed when the expression, you will no longer do not provide a default branch. Note, sealed modifier implied that this class is a class open, you no longer need to explicitly add open modifier. When you are sealed class (Expr) subclass to add a new sub-class of the time, when an expression that returns a value fail to compile. We need to add a new sub-class of a new branch.

Statement constructor

Java can be declared in a class one or more constructors. Kotlin is similar, the knowledge to make a little modification: the distinction between primary construction method (usually the main and simple method to initialize the class, and the outside of the class declaration) and from a constructor (class declared inside the body).

Main constructor

Main constructor is typically added when the class declaration class name directly behind the bracket, then declare the corresponding parameters are declared in parentheses, they are enclosed in parentheses statement block constructor called shots. code show as below:

//以下代码即表示声明了一个带有nickname参数的主构造方法
class User(val nickname: String)

The above code is the easiest way to master a constructor declaration, but in fact, the above code Kotlin will eventually help us to deal with, we look at how it works:

class User constructor(_nickname: String) {
    val nickname: String

    init {
        nickname = _nickname
    }
}

In the above example, there have been constructor and init two new keywords. constructor is used to start a keyword or a declaration from a main constructor constructor. for introducing a keyword init initializes block. Because the main constructor syntax restrictions, can not contain initialization code, which is why you want to use reason initialization statement block. If you reason, you can also declare multiple initialization block of statements in a class.

Of course, if the initialization statements like the above code block just an assignment operator, then we may not need the nickname = _nickname this line of code assignment of initialization statement on the block, because it can be combined with the declaration nickname attribute. If no primary constructor or annotation visibility modifiers, the same keyword can be omitted constructor. The final code can be simplified as follows:

class User constructor(_nickname: String) {
  val nickname = _nickname
}

These three main constructor statement is the same way, only the first using the most concise syntax.

The method may be configured as the main function parameters as constructor parameter declaration is a default value:

class User(val nickname:String = "xiaochao",val isSubscribed:Boolean = true)

fun main(args: Array<String>) {
    val user1 = User("hello")
    val user2 = User(isSubscribed = false)
    val user3 = User("kotlin",false)
}

If your class has a parent class, the main constructor initializes the parent class also requires:

open class User(val nickname: String)

class MyUser(nickname:String) :User(nickname){
    //...
}

If you do not declare any constructor for a class, will generate a default constructor does nothing, if there is a subclass inherits the class, then subclass the default constructor of the class must be called explicitly, even if it has no parameters:

open class Button

class RadioButton: Button() {
    //...
}

If you want to make sure your class is not instantiated other code, the constructor must be marked private :

class User private constructor(val name:String){
    //...
}

From Constructor

Above we said that when a class does not define any constructor time, Kotlin will automatically generate a default main constructor, so in the subclass inherits the class always needs a default constructor calls the parent class display. When and if a class constructor is defined from, but is not defined when the main construction method, it does not automatically Kotlin its default constructor to generate a main. Call the superclass constructor from the subclass inherits the time when you want to display. code show as below:

open class User{
    constructor(userName:String){
        //...
    }
    constructor(userName: String,age:Int){
        //...
    }
}

class MyUser:User{
    constructor(userName:String):super(userName) // 调用父类的从构造方法
    constructor(userName: String,age:Int):super(userName,age) // 调用父类的从构造方法
}

//当然,这里也可以像之前一样通过在继承的时候就显示调用父类的从构造方法,如下
//class MyUser(userName: String) : User(userName) {
//  
//}

From the above we can see that call the parent class constructor correspondence by using the super () keyword within a subclass. At the same time, it can be like, like in Java, by using this () keyword, you call another constructor from a class constructor. code show as below:

class MyUser : User {
    //通过this调用自己的两个参数的构造方法
    constructor(userName: String) : this(userName, 0)
    
    constructor(userName: String, age: Int) : super(userName, age)
}

By accessing support getter or setter field (field)

class User(val name:String){
    var address: String = "unspecified"
        set(value) {
            println("$field --->${value.trimIndent()}")
            field = value
        }
}

fun main(args: Array<String>) {
    val user = User("xiaochao")
    user.address = "shanghai"
    user.address = "beijing"
}
>>> unspecified --->shanghai
>>> shanghai --->beijing

The above code implements a value may be stored and may provide additional logic in the attribute value is accessed and modified. To support this case, we need to be able to access its support field (field) from the property accessor. Setter in the function body, using a special identifier field to access the value of field support. In getter, you can only read values; and in the setter in both read and it can also modify it.

There is no property to support the field and what is the difference?
The way to access the properties do not depend on whether it contains support field. If you explicitly reference or use the default access is implemented, the compiler generates support field is a property. If you provide a custom accessor implementation and does not use field , support field will not be presented.

Sometimes the need to modify the default access control implementation, but need to modify its visibility. The following sample code:

class LengthCounter {
    var counter: Int = 0
        private set

    fun addWord(word: String) {
        counter += word.length
    }
}

fun main(args: Array<String>) {
    val lengthCounter = LengthCounter()
    lengthCounter.addWord("HelloWorld!")
    println(lengthCounter.counter)
}

>>> 11

Data class data

Data class with the data keyword modified class represents data has been rewritten Kotlin class equal, hashCode, toString methods. Examples are as follows:

class Student(val name:String,val age:Int)

data class DataStudent(val name:String ,val age:Int)

fun main(args: Array<String>) {
    val xiaoming1 = Student("xiaoming",20)
    val xiaoming2 = Student("xiaoming",20)
    val xiaomingSet = hashSetOf(xiaoming1,xiaoming2)
    println(xiaoming1.toString())
    println(xiaoming1==xiaoming2)
    println("size = ${xiaomingSet.size} ${xiaomingSet.contains(Student("xiaoming",20))}")
    println("----------------------------")

    val dataxiaoming1 = DataStudent("xiaoming",20)
    val dataxiaoming2 = DataStudent("xiaoming",20)
    val dataxiaomingSet = hashSetOf(dataxiaoming1,dataxiaoming2)
    println(dataxiaoming1.toString())
    println(dataxiaoming1==dataxiaoming2)
    println("size = ${dataxiaomingSet.size} ${dataxiaomingSet.contains(DataStudent("xiaoming",20))}")
}

>>>chapter04.Student@3fee733d
>>>false
>>>size = 2 false
>>>----------------------------
>>>DataStudent(name=xiaoming, age=20)
>>>true
>>>size = 1 true

And immutable data classes: copy () method

Although the data class attribute does not have to be a val - can also use var - it is strongly recommended to use only val attribute, so that the sample data class immutable. If you want to use such an example as HashMap key or similar containers, which is an essential requirement, because if not, the object is used as the key changes after being added to the vessel, the vessel may enter into an ineffective state . Immutable objects is also easier to understand, especially in multi-thread: Once an object is created, it will remain in the initial state, do not worry when your code works the other thread modifies the value of the object.

To make use of immutable data object classes easier, Kotlin compiler which generates a plurality methods: one example of the class allows the copy, and modify the values ​​of certain properties while the copy. Create a copy of the modified example is usually a good choice: have a separate copy of the life cycle and does not affect the position of code references the original instance. Here is the method implemented manually copy looks like:

class Student(val name:String,val age:Int){
    fun copy(name:String = this.name,age:Int = this.age) = Student(name,age)
}

fun main(args: Array<String>) {
    val student = Student("xiaoming",22)
    println(student.copy(age = 23).age)
}
>>> 23

This is the data modifier is how to use the value object class is more convenient reasons.

Class delegate: by keyword

In development, we may often need to add some behavior to other classes, even though it was not designed to be extensible. A common implementation is famous for decoration mode. The essence of this model is to create a new class that implements the same class as the original and the original instance of the class interface to save as a field. The method has the same behavior as the original class without being modified, only you need to be forwarded directly to the instance of the original class.

One disadvantage of this approach is the need for a considerable amount of boilerplate code. For example, here is how much code you need to implement a simple interface to get from the Collection of decorative such, even if you do not need to modify any of the acts:

class DelegatingCollection<T> : Collection<T> {
    private val innerList = arrayListOf<T>()
    override val size: Int
        get() = innerList.size

    override fun contains(element: T): Boolean = innerList.contains(element)

    override fun containsAll(elements: Collection<T>): Boolean = innerList.containsAll(elements)

    override fun isEmpty(): Boolean = innerList.isEmpty()

    override fun iterator(): Iterator<T> = innerList.iterator()
}

Kotlin will be commissioned as a language level of functionality to do a first-class support, whenever implements an interface, you can use by keywords to delegate implementation of an interface to another object. By the following keywords to achieve the above by using the same function code:

class DelegatingCollection<T>(innerList: Collection<T> = ArrayList<T>())
    : Collection<T> by innerList

Now, when you need to modify the behavior of certain methods, you can rewrite them so that your method will be called instead of using the method generates. You can retain satisfied delegate to the default instance of internal implementation.

class CountingSet<T>(val innerList: MutableCollection<T> = HashSet<T>()) : MutableCollection<T> by innerList {
    var objectsAdd = 0
    override fun add(element: T): Boolean {
        objectsAdd++
        return innerList.add(element)
    }

    override fun addAll(elements: Collection<T>): Boolean {
        objectsAdd += elements.size
        return innerList.addAll(elements)
    }
}

fun main(args: Array<String>) {
    val cset = CountingSet<Int>()
    cset.addAll(listOf(1, 2, 2))
    println("${cset.objectsAdd}     ${cset.size}")
}

>>> 3     2

object: to declare a class and create an instance combination

object Use keywords There are three main scenarios:

  • Defined singleton
  • Alternative Java anonymous inner classes
  • Creating an object associated with the companion

Object declaration: Creating a singleton by object

object SingleInstance {
    var name: String? = null
    fun initName() {
        name = "hello world"
    }
}

fun main(args: Array<String>) {
    val a = SingleInstance
    val b = SingleInstance
    SingleInstance.initName()
    b.initName()
    println(a === b)
}
>>> true

And classes, an object declaration statement may contain attributes, methods, and other initialization statement block. The only method of construction is not permitted (including a main configuration for methods and constructors from). And common samples of different objects declared at the time defined immediately created. The same statement can be inherited from the object classes and interfaces.

Alternative Java anonymous inner classes with object

interface Click{
    fun onClick()
    fun onLongClick()
}

fun main(args: Array<String>) {
    val clickClistener = object :Click{
        override fun onClick() {
        }

        override fun onLongClick() {
        }
    }
}

When using object to represent an anonymous inner class, with different object declaration, an anonymous object is not a single example, the expression is executed each time an object creates a new object instance.

Associated Objects

Kotlin in the class can not have a static member; a part of the static keyword in Java is not Kotlin language. As an alternative, Kotlin rely package-level functions and object declarations, in most cases, it is recommended to use the top-level function, but can not access the private members of the class of top-level function. This time it can consider to solve this problem by companion objects. In Kotlin, the object through the associated companion marked. The syntax is as follows:

class A {
    companion object {
        const val name = "Hello world"
        fun bar() {
            println("Companion object called")
        }
    }
}

fun main(args: Array<String>) {
    A.bar()
    A.name
}

Companion object is a common object is declared in the class, it can have a name, or implement an interface has spread function or property.

interface Click{   
        fun onClick()
    fun onLongClick()
}

class A {
    companion object Inner : Click { // 实现接口
        override fun onClick() {
        }

        override fun onLongClick() {
        }

        const val name = "Hello world"
        fun bar() {
            println("Companion object called")
        }
    }
}

fun A.Inner.kuozhan() { //扩展函数
    println("这是伴生对象的扩展函数")
}

fun main(args: Array<String>) {
    A.bar()
    A.name
    A.Inner.bar()
    A.Inner.name
    A.Inner.kuozhan()
}

to sum up

  1. Kotlin interface is similar to Java, but may contain a default implementation and attributes (Java began to support the 8th edition).
  2. All statements are the default final and public the.
  3. To make the declaration is not final , and mark it as open .
  4. internal A statement seen in the same module.
  5. The default is not a nested class inside the class. Use inner keyword to declare inner classes, class references to external storage.
  6. sealed Subclass of the class can only be nested in its own statement (Kotlin1.1 allow subclasses placed anywhere in the same file).
  7. Initializer block and configured from a class instance initialization method provides flexibility.
  8. Use field the identifier in the reference field attributes support access method body vessel.
  9. Class provides data generated by the compiler equal, hashCode, toString, copy, and other methods.
  10. Help avoid many similar class delegate delegate methods appear in the code.
  11. The method object declaration is defined in a single embodiment Kotlin class.
  12. Companion object (bonding property with the package-level functions together) replaces the Java static methods and fields.
  13. Objects associated with other objects, you can implement an interface, it can also have extended functions and properties.
  14. An object expression is Kotlin for alternatives in Java anonymous inner class, and adds the ability to modify and create variables defined in the role of objects in the domain, such as the ability to implement multiple interfaces and other functions.

Guess you like

Origin www.cnblogs.com/xxiaochao/p/11497252.html