Глава IV (классы, объекты и интерфейсы)

Котлин классы и интерфейсы с классами и интерфейсами Java еще немного отличается. Например: интерфейс может содержать объявление свойства. В отличие от Java, Котлин по умолчанию объявляется final и public . Кроме того, класс по умолчанию не вложен внутренним классов: они не содержат скрытую ссылку на его внешний класс.

Котлин интерфейс

8 аналогично интерфейсу котлинский Java: они могут содержать не-абстрактные определения и методы, реализованные абстрактные методы. Разница заключается в том, что с Java 8, вам нужно Java 8 отмеченные ключевые слова по умолчанию в такой реализации, и Котлин нет особых комментариев: нужно только предоставить тело метода. Пример:

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

Выше основное определение пути интерфейс, то наше понимание того , как реализовать интерфейс? Котлин использовать двоеточие после имени класса , а не в Java расширяет и реализует ключевые слова. Подобно Java, класс может реализовать любое количество интерфейсов, но могут наследовать только класс. И Java @Override аннотации похожи, Котлин с использованием override модификатор переписан для обозначения свойств родительского класса или интерфейса метода. В отличие от Java используется в Котлин override модификатором является обязательным. 

open class TextView : Clickable {

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

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

}

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

Так что, если у нас есть интерфейс Focusable, есть один и тот же Clickable метод интерфейса Showoff на этом интерфейсе, когда TextView наследовать оба интерфейса, когда в конце концов, как достичь этих двух методов?

open class TextView : Clickable,Focusable {

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

Модификаторы

открыть

Java, позволяет создать подкласс класса и переопределить любой метод, если явно не использовать final мечение ключевого слова. В Котлин, по умолчанию final , то есть, по умолчанию не передается по наследству или переопределены. Если вы хотите , чтобы создать подкласс класса, вы должны использовать open модификаторы для определения класса. Кроме того, необходимо для каждого свойства или метода могут быть переопределены , чтобы добавить open модификаторы.

Примечание: Если переопределить базовый класс или члены интерфейса переписали члены одного и то же по умолчанию открыты. Если вы хотите изменить это поведение, остановите свой подкласс класса переписать реализацию, вы можете явно помечены как член переписывания final .

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

Аннотация

В Котлин, так же , как Java, класс может быть объявлен как abstract из этого класса не может быть создан. Абстрактный класс , как правило , содержит некоторые , не оправдались , и должны быть переопределены в подклассе абстрактных членов. Абстрактные члены всегда открыты в , нет необходимости явно использовать открытый классификатор.

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

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

    }

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

    }
}

Доступ модификаторы значения класса

Модификаторы Соответствующие члены комментарий
окончательный Это не может быть переписан Члены класса по умолчанию
открыть Его можно переписать Чтобы ясно показать
Аннотация Необходимо переписать Используйте только в абстрактном классе, абстрактные члены не могут быть реализованы
переопределение Переопределить родительский класс или интерфейс член Если нет финального шоу, открыто переопределения по умолчанию члена

Видимость модификатор (общественность и т.д.)

Аналогично видимости модификаторов в Java Котлин в. Также вы можете использовать открытые, защищенные и частные модификаторы. Но видимость по умолчанию не то же самое: Если опустить классификатор, по умолчанию является публичное заявление.
Java в видимости по умолчанию - частный пакет, не используется в Котлин в. Котлинский только пакет , как способ организации кода , используемого в пространстве имен, а не в качестве его контроля видимости.
В качестве альтернативы, котлинский обеспечивает новый модификатор, internal указывающий «видна только внутри модуля.» Модуль представляет собой группу Котлин файлов скомпилированы вместе. Это может быть модуль IDEA Intellij, проект Затмения проекта Gradle или Maven или Ant вызовов задач с помощью набора скомпилированных файлов.

Kotlin модификаторы видимости

Модификаторы Члены класса Заявления на высшем уровне
pulbic (по умолчанию) Все Видное место Все Видное место
внутренний Модули можно увидеть Модули можно увидеть
защищенный Подкласс видно Не защищенный модификатор
частный Видимый класс Файл виден

Пример: Следующие функции каждой строки giveSpeech попытки нарушить правила видимости. Ошибка возникает во время компиляции.

 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"的
  }

Чтобы решить эту проблему, в приведенном выше примере, как giveSpeech изменил внутреннюю функцию, вы можете также изменить тип публики, конечно, если вы хотите получить доступ к «орать и методы шепота, вам необходимо прочитать эти два метода общественных или внутренние а.

Внутренние вложенные классы и классы

Подобно Java, вы можете объявить класс в другом классе в Котлин в. Разница заключается в том Котлин вложенный класс не может получить доступ к экземпляру внешнего класса, если вы сделаете специальный запрос.
В Котлин не было показано , с теми же вложенными модификаторов класса в Java статических вложенных классов. Включите его во внутренний класс держать ссылку на внешний класс, то вам нужно использовать inner модификаторы. Примеры внешних эталонных классов в котлинский Java синтаксис также различны. Вы должны использовать этот @ OuterClass посетить OuterClass класс от InnerClass.

И внутренние вложенные классы в классе Java соответствует Котлин

Класс объявлены в другом классе B, В Java В Котлин
Уплотненный класс (класс не хранится во внешнем виде ссылки) статический класс А класс А
Внутренний класс (внешние ссылки памяти класса) класс А Внутренний класс А

запечатаны запечатанный класс: Определить запретную структуру наследования

При использовании структуры для выполнения, если выражения, Котлин компилятор будет применять вариант проверки по умолчанию. Например: Предположим, Expr родительский класс имеет два подкласса: представляющие числа Num, и представляет собой сумму двух выражений два SUM. Обрабатывать все возможные подклассы, безусловно, очень удобно, когда выражение, но должен обеспечить еще ветвь, чтобы иметь дело с отсутствием какой-либо другой отрасли может соответствовать:

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")
}

В приведенном выше примере, то еще , потому что условия не могут возвращать осмысленное значение, непосредственно он вызывает исключение. Если вы добавляете новый подкласс, компилятор не может найти место , чтобы изменить. Если вы забыли добавить новую ветвь, она будет выбрать другое отделение по умолчанию, что может привести к потенциальной ошибке.
Котлин обеспечивает решение этой проблемы: герметичный класс. Добавить запечатанный модификатор родительского класса, установить жесткие ограничения на подклассы могут быть созданы. Все прямые подклассы родительского класса должны быть вложены.

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)
}

Если вы имеете дело с подклассом класса во всех запечатанный, когда выражение, вы больше не будете не обеспечивают ветви по умолчанию. Обратите внимание, запечатаны модификатор подразумевается, что этот класс является классом открытым, вам больше не нужно явно добавить открытый модификатор. Когда вы запечатаны класс (Expr) подкласс, чтобы добавить новый подкласс времени, когда это выражение, которое возвращает значение не компилировать. Нам нужно добавить новый подкласс новой отрасли.

Заявление конструктора

Java может быть объявлен в классе один или несколько конструкторов. Котлин похоже, знания, чтобы сделать небольшое изменение: различие между методом первичного строительства (как правило, основной и простой метод для инициализации класса, и за пределами объявления класса) и из конструктора (класс объявленный внутри тела).

Главный конструктор

Главный конструктор обычно добавляются, когда имя класса Объявления класса непосредственно за скобками, а затем объявить соответствующие параметры объявлены в скобках, они заключены в круглых скобках Участок_программы конструктор называемых кадрах. Код выглядит следующим образом:

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

Приведенный выше код является самым простым способом освоить декларацию конструктора, но на самом деле, приведенный выше код Котлин, в конечном счете поможет нам справиться, мы посмотрим, как это работает:

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

    init {
        nickname = _nickname
    }
}

В приведенном выше примере, были constructor и init два новых ключевых слов. Конструктор используется для запуска ключевого слова или заявления от главного конструктора конструктора. для введения ключевого слова инициализации инициализирует блок. Поскольку основные ограничения синтаксиса конструктора, не может содержать код инициализации, поэтому вы хотите использовать причину инициализации операторный блок. Если рассуждать, то вы можете также объявить несколько блоков инициализации операторов в классе.

Конечно, если операторы инициализации, как выше блока кода просто оператор присваивания, то мы можем не нужен ник = _nickname эту строку присвоения кода инициализации заявления на блоке, поскольку он может быть объединен с атрибутом декларация псевдонима. Если первичный конструктор или видимости аннотации модификаторов, то же ключевое слово можно опустить конструктор. Окончательный код можно упростить следующим образом:

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

Эти три основных оператор конструктора является таким же образом, только первый, используя самый краткий синтаксис.

Способ может быть сконфигурирован в качестве основных параметров функции, как объявление параметра конструктора значения по умолчанию:

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)
}

Если ваш класс имеет родительский класс, главный конструктор инициализирует родительский класс требует также:

open class User(val nickname: String)

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

Если вы не объявлять любой конструктор для класса, будет генерировать конструктор по умолчанию ничего не делает, если есть подкласс наследует класс, а затем подкласс конструктор по умолчанию класса должен быть вызван явно, даже если он не имеет параметры:

open class Button

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

Если вы хотите , чтобы убедиться , что ваш класс не создается экземпляр другого кода, конструктор должен быть отмечен private :

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

С застройщиком

Выше мы говорили, что если класс не определяет время конструкторы, Котлин будет автоматически генерировать главный конструктор по умолчанию, так что в подклассе наследует класс всегда нуждается в конструкторе по умолчанию вызывает отображение родительского класса. Когда и если конструктор класса определяется из, но не определен, когда основной метод строительства, это автоматически не Котлин его конструктор по умолчанию для создания основной. Вызов конструктора суперкласса из подкласса наследует время, когда вы хотите отобразить. Код выглядит следующим образом:

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) {
//  
//}

Из сказанного выше видно, что вызов конструктора родительского класса корреспонденцию с помощью супер () ключевое слово в подклассе. В то же время, это может быть, как, например, в Java, с помощью этого () ключевого слова, вызвать другой конструктор из конструктора класса. Код выглядит следующим образом:

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

При доступе поддержки геттер или сеттера поле (поле)

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

Выше код реализует значение может быть сохранено и может обеспечить дополнительную логику в значении атрибута использовать и модифицировать. Для того, чтобы поддержать этот случай, мы должны быть в состоянии получить доступ к его полю поддержки (поле) из свойства аксессора. SETTER в теле функции, используя специальное поле идентификатора для доступа к значению поля поддержки. В добытчике, вы можете только читать значения, а в инкубаторе в чтении и он также может изменить его.

Там нет никакой собственности , чтобы поддержать поле и в чем разница?
Способ получить доступ к свойству не зависит от того, содержит ли поле поддержки. Если вы явно ссылаться или использовать доступ по умолчанию будет реализован, то компилятор генерирует поддержку поле является свойством. Если вы обеспечите реализацию пользовательских аксессора и не используете field , поддержку на местах не будут представлены.

Иногда необходимо изменить реализацию контроля доступа по умолчанию, но необходимо изменить его видимость. Следующий пример кода:

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

класс данных с data классом ключевого слова модифицирована представляет данные были переписаны Котлин класса равны, методы хэш - код, ToString. Примерами являются следующие:

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

И неизменные классы данных: копия () метод

Хотя атрибут класса данных не должны быть Вэл - также можно использовать вар - настоятельно рекомендуется использовать только VAL атрибут, так что класс данных выборки неизменны. Если вы хотите использовать такой пример как ключ HashMap или подобные контейнеры, что является необходимым требованием, потому что если нет, то объект используется в качестве ключевых изменений после добавления в сосуд, судно может войти в неэффективное состояние , Неизменные объекты также легче понять, особенно в многопоточном: После того, как объект будет создан, он будет оставаться в исходном состоянии, не волнуйтесь, если ваш код работает другой поток изменяет значение объекта.

Для того, чтобы использовать неизменные классы объектов данных проще, котлинский компилятор, который генерирует методы мажоритарные: один из примеров класса позволяет копировать и изменять значение некоторых свойств в то время как копию. Создайте копию модифицированного примера, как правило, хороший выбор: иметь отдельную копию жизненного цикла и не влияет на позицию кода ссылается на исходный экземпляр. Вот метод, реализованный вручную скопировать выглядит следующим образом:

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

Это модификатор данных, как использовать значение объект класса является более удобными причинами.

Класс Делегат: по ключевому слову

В процессе развития, мы можем часто нужно добавить поведение к другим классам, даже если он не был разработан, чтобы быть расширяемым. Общая реализация известна режим украшения. Суть этой модели заключается в создании нового класса, который реализует тот же класс, как оригинал и оригинальный экземпляр интерфейса класса, чтобы сохранить в качестве поля. Метод имеет такое же поведение, что и исходный класс без модифицируются, только вы должны быть направлены непосредственно на экземпляр исходного класса.

Одним из недостатков этого подхода является необходимость значительного количества шаблонного кода. Например, вот сколько коды вам нужно реализовать простой интерфейс, чтобы получить из коллекции декоративных, таких, даже если вам не нужно изменить какие-либо из действий:

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()
}

Котлин будет введен в эксплуатацию в качестве уровня языка функциональных возможностей, чтобы сделать поддержку первого класса, когда реализует интерфейс, вы можете использовать by ключевые слова , чтобы делегировать реализацию интерфейса к другому объекту. По следующим ключевым словам для достижения выше, используя тот же код функции:

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

Теперь, когда вам нужно изменить поведение некоторых методов, вы можете переписать их так, что ваш метод будет вызываться вместо того, чтобы использовать метод генерирует. Вы можете сохранить удовлетворенный делегат к экземпляру по умолчанию внутренней реализации.

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 Используйте ключевые слова Там три основных сценария:

  • Определено одноточечно
  • Альтернативные Java анонимные внутренние классы
  • Создание объекта, связанного с компаньоном

Объявление объекта: Создание синглтона по объекту

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

И классы, объявление объекта оператор может содержать атрибуты, методы и другие инициализации блока операторов. Единственный метод строительства не допускается (в том числе основной конфигурации для методов и конструкторов из). А общие образцы различных объектов, объявленных в то время определены немедленно созданы. То же самое утверждение может быть унаследовано от классов объектов и интерфейсов.

Альтернативные Java анонимные внутренние классы с объектом

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

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

        override fun onLongClick() {
        }
    }
}

При использовании объекта для представления анонимного внутреннего класса, с различным описанием объекта, анонимный объект не единственным примером, выражение выполняется каждый раз, когда объект создает новый экземпляр объекта.

Связанные объекты

Котлин в классе не может иметь статический член, часть статического ключевого слова в Java не Котлин язык. В качестве альтернативы, Котлин полагается функции уровня пакета и декларации объектов, в большинстве случаев, рекомендуются использовать функцию верхнего уровня, но не может получить доступ к закрытым членам класса функции верхнего уровня. На этот раз он может рассмотреть , чтобы решить эту проблему с помощью объектов - компаньонов. В Котлин, объект через связанный с companion пометкой. Синтаксис выглядит следующим образом :

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 является общим объект объявлен в классе, он может иметь имя, или реализовать интерфейс распространял функцию или свойство.

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()
}

резюме

  1. Интерфейс Котлин похож на Java, но может содержать реализацию по умолчанию и атрибуты (Java начали поддерживать 8-е издание).
  2. Все заявления по умолчанию final и public .
  3. Для того, чтобы декларация не final , и пометить его как open .
  4. internal Заявление видели в том же модуле.
  5. По умолчанию не вложенный класс внутри класса. Используйте inner ключевое слово , чтобы объявить внутренние классы, ссылки на класс на внешнее устройство хранения.
  6. sealed Подкласс класса могут быть вложены только в своем заявлении (Kotlin1.1 позволяют подклассы, расположенных в любом месте в том же файле).
  7. Инициализатора блок и сконфигурировано из метода инициализации экземпляра класса обеспечивает гибкость.
  8. Используйте field идентификатор в поле атрибуты опорного доступ поддерживает метод корпус сосуда.
  9. Класс предоставляет данные, генерируемые компилятором равной, хэш-код, ToString, копировать, и другими методами.
  10. Помогите избежать многих подобных методов класса делегата делегат появляются в коде.
  11. Описание объекта метод определен в одном варианте осуществления котлинского класса.
  12. Companion объект (склеивание свойство с функциями на уровне пакета вместе) заменяет Java статические методы и поля.
  13. Объекты, связанные с другими объектами, вы можете реализовать интерфейс, он также может иметь расширенные функции и свойства.
  14. Выражение объекта является Котлином альтернативы в Java анонимного внутреннего класса, а также добавляет возможность изменять и создавать переменные, определенные в ролях объектов в области, такие, как способность реализовать несколько интерфейсов и другие функции.

рекомендация

отwww.cnblogs.com/xxiaochao/p/11497252.html
рекомендация