Kotlin — это современный язык программирования, который компилируется в байт-код Java. Он бесплатный и с открытым исходным кодом и обещает сделать кодирование для Android еще более увлекательным.
В предыдущей статье вы узнали о расширенном использовании функций, таких как функции расширения, замыкания, функции высшего порядка и встроенные функции в Kotlin.
В этой статье вы познакомитесь с объектно-ориентированным программированием в Kotlin, изучив классы: конструкторы и свойства, приведение типов и более продвинутые функции классов, которые Kotlin упрощает.
1. Классы
Класс — это программный блок, который группирует функции и данные для выполнения некоторых связанных задач. Мы объявляем класс в Kotlin, используя ключевое слово class
— аналогично Java.
1
|
class Book
|
Предыдущий код является самым простым объявлением класса — мы только что создали пустой класс с именем Book
. Мы все еще можем создать экземпляр этого класса, даже если он не содержит тела, используя конструктор по умолчанию.
1
|
val book = Book()
|
Как вы можете заметить в приведенном выше коде, мы не использовали ключевое слово new
для создания экземпляра этого класса — как обычно в других языках программирования. new
не ключевое слово в Kotlin. Это делает наш исходный код сжатым при создании экземпляра класса. Но имейте в виду, что для создания класса Kotlin в Java потребуется new
ключевое слово.
1
2
|
// In a Java file
Book book = new Book()
|
Конструкторы класса и свойства
Давайте посмотрим, как добавить конструктор и свойства в наш класс. Но сначала давайте посмотрим на типичный класс в Java:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
/* Java */
public class Book {
private String title;
private Long isbn;
public Book(String title, Long isbn) {
this.title = title;
this.isbn = isbn;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Long getIsbn() {
return isbn;
}
public void setIsbn(Long isbn) {
this.isbn = isbn;
}
}
|
Глядя на наш класс модели Book
выше, мы имеем следующее:
- два поля:
title
иisbn
- один конструктор
- методы получения и установки для двух полей (к счастью, IntelliJ IDEA может помочь нам сгенерировать эти методы)
Теперь давайте посмотрим, как вместо этого можно написать предыдущий код на Kotlin:
01
02
03
04
05
06
07
08
09
10
|
/* Kotlin */
class Book {
var title: String
var isbn: Long
constructor(title: String, isbn: Long) {
this.title = title
this.isbn = isbn
}
}
|
Довольно аккуратный класс! Теперь мы сократили количество строк кода с 20 до просто 9. Функция constructor()
называется вторичным конструктором в Kotlin . Этот конструктор эквивалентен конструктору Java, который мы вызывали при создании экземпляра класса.
В Котлине нет понятия поля, с которым вы, возможно, знакомы; вместо этого он использует понятие «свойства». Например, у нас есть два изменяемых свойства (чтение-запись), объявленных с ключевым словом var
: title
и isbn
в классе Book
. (Если вам нужно освежить в памяти переменные в Kotlin, пожалуйста, посетите первый пост в этой серии: Переменные, Базовые типы и Массивы ).
Удивительно, что геттеры и сеттеры для этих свойств автоматически генерируются для нас компилятором Kotlin. Обратите внимание, что мы не указали никаких модификаторов видимости для этих свойств — поэтому по умолчанию они общедоступны. Другими словами, к ним можно получить доступ откуда угодно.
Давайте посмотрим на другую версию того же класса в Kotlin:
1
2
3
4
5
6
7
8
9
|
class Book constructor(title: String, isbn: Long) {
var title: String
var isbn: Long
init {
this.title = title
this.isbn = isbn
}
}
|
В этом коде мы удалили вторичный конструктор. Вместо этого мы объявили конструктор в заголовке класса, который называется первичным конструктором . У первичного конструктора нет места для размещения блока кода, поэтому мы используем модификатор init
для инициализации входящих параметров из первичного конструктора. Обратите внимание, что блок кода init
выполняется сразу при создании экземпляра класса.
Как вы можете видеть, наш код все еще содержит много шаблонов. Давайте уменьшим это далее:
1
|
class Book constructor(var title: String, var isbn: Long)
|
Наш класс Book
теперь представляет собой всего одну строку кода. Это действительно круто! Обратите внимание, что в списке параметров первичного конструктора мы определили наши изменяемые свойства: title
и isbn
непосредственно внутри первичного конструктора с помощью ключевого слова var
.
Мы также можем добавить значения по умолчанию к любому из свойств класса прямо внутри конструктора.
1
|
class Book constructor(var title: String = «default value», var isbn: Long)
|
Фактически, мы также можем опустить ключевое слово constructor
, но только если оно не имеет модификатора видимости ( public
, private
или protected
) или каких-либо аннотаций.
1
|
class Book (var title: String = «default value», var isbn: Long)
|
Очень аккуратный класс, я должен сказать!
Теперь мы можем создать экземпляр класса следующим образом:
1
2
|
val book = Book(«A Song of Ice and Fire», 9780007477159)
val book2 = Book(1234) // uses the title property’s default value
|
Доступ и настройка свойств
В Kotlin мы можем получить свойство с помощью book
объектов класса, за которой следует разделитель точек .
, то название свойства название. Этот краткий стиль доступа к свойствам называется синтаксисом доступа к свойствам . Другими словами, нам не нужно вызывать метод получения свойства для доступа или вызывать метод установки для установки свойства в Kotlin — как мы делаем в Java.
1
|
println(book.title) // «A Song of Ice and Fire»
|
Поскольку свойство isbn
объявлено с ключевым словом var
(чтение-запись), мы также можем изменить значение свойства, используя оператор присваивания =
.
1
2
|
book.isbn = 1234
println(book.isbn) // 1234
|
Давайте посмотрим на другой пример:
1
2
3
4
5
6
7
8
|
class Book (
var title: String,
val isbn: Long
)
val book = Book(«A Song of Ice and Fire», 9780007477159)
book.isbn = 1234 // error: read-only property
book.title = «Things Fall Apart» // reassigned title with value
|
Здесь мы обновили параметр isbn
чтобы он стал неизменным (только для чтения) — с помощью ключевого слова val
. Мы создали экземпляр экземпляра book
класса и присвоили свойству title
значение «Things Fall Apart». Обратите внимание, что когда мы попытались переназначить значение свойства isbn
на 1234
, компилятор пожаловался. Это связано с тем, что свойство является неизменным, поскольку оно определено с помощью ключевого слова val
.
Совместимость с Java
Имейте в виду, что, объявив параметр с модификатором var
внутри основного конструктора, компилятор Kotlin (за кулисами) помог нам сгенерировать оба метода доступа: getter и setter. Если вы используете val
, он будет генерировать только геттер.
1
2
3
4
5
|
/* Kotlin */
class Book (
var title: String,
val isbn: Long
)
|
Это означает, что вызывающие Java могут просто получить или установить поле свойства, вызвав метод setter или getter свойства соответственно. Помните, что это зависит от модификатора, используемого для определения свойства Kotlin: var
или val
.
1
2
3
4
5
6
7
8
|
/* Java */
Book book = new Book(«A Song of Ice and Fire», 9780385474542)
println(book.getTitle()) // «A Song of Ice and Fire»
book.setTitle(«Things Fall Apart») // sets new value
println(book.getTitle()) // «Things Fall Apart»
book.getIsbn() // 9780385474542
book.setIsbn(4545454) // won’t compile
|
Пользовательские геттеры и сеттеры
В этом разделе я покажу вам, как создать собственные средства доступа (геттеры и сеттеры) для свойства в Kotlin, если хотите. Создание пользовательского установщика может быть полезно, если вы хотите проверить или проверить значение, прежде чем оно будет установлено в свойство класса. И пользовательский метод получения свойств может быть полезен, когда вы хотите изменить или изменить значение, которое должно быть возвращено.
Создание пользовательского сеттера
Поскольку мы хотим создать свой собственный метод получения или установки для свойства, мы должны определить это свойство в теле класса вместо заголовка конструктора.
1
2
3
|
class Book (val isbn: Long) {
var title = «default value»
}
|
Вот почему мы переместили изменяемое свойство title
(чтение-запись) в тело класса и присвоили ему значение по умолчанию (иначе оно не будет компилироваться).
1
2
3
4
5
6
7
8
9
|
class Book (val isbn: Long) {
var title = «default value»
set(value) {
if (!value.isNotEmpty()) {
throw IllegalArgumentException(«Title must not be empty»)
}
field = value
}
}
|
Вы можете видеть, что мы определили наш собственный метод set(value)
метода set(value)
для title
прямо под определением свойства — обратите внимание, что вы не можете изменить эту сигнатуру метода set()
потому что это то, что компилятор ожидает как функцию установки собственного свойства.
Значение параметра, передаваемое в метод set
представляет собой фактическое значение, которое было присвоено свойству пользователями — вы можете изменить имя параметра, если хотите, но value
гораздо предпочтительнее. Мы проверили value
, проверив, является ли оно пустым. Если пусто, остановите выполнение и сгенерируйте исключение; в противном случае переназначьте значение специальной переменной field
.
Это специальное field
переменной поля внутри метода set
является псевдонимом для базового поля свойства — вспомогательное поле — это просто поле, которое используется свойствами, когда вы хотите изменить или использовать данные этого поля. В отличие от value
, вы не можете переименовать эту специальную переменную field
.
Создание пользовательского геттера
Очень легко создать собственный геттер для свойства в Котлине.
1
2
3
4
5
6
7
|
class Book (val isbn: Long) {
var title = «default value»
//… set method
get() {
return field.toUpperCase()
}
}
|
Внутри метода get
мы просто возвращаем измененное field
— в нашем случае мы возвращали название книги в верхнем регистре.
1
2
3
4
|
val book = Book(9780007477159)
book.title = «A Song of Ice and Fire»
println(book.title) // «A SONG OF ICE AND FIRE»
println(book.isbn) // 9780007477159
|
Обратите внимание, что каждый раз, когда мы устанавливаем значение для свойства title
, выполняется его блок метода set
— то же самое касается метода get
каждый раз, когда мы его извлекаем.
Если вы хотите узнать о функциях-членах для класса Kotlin (вид функции, которая определена внутри класса, объекта или интерфейса), посетите публикацию « Больше возможностей с функциями» в этой серии.
Подробнее о конструкторах
Как я уже говорил ранее, у нас есть два типа конструкторов в Kotlin: первичный и вторичный. У нас есть свобода объединить их обоих в один класс — как вы можете видеть в следующем примере:
1
2
3
4
5
6
7
|
class Car(val name: String, val plateNo: String) {
var new: Boolean = true
constructor(name: String, plateNo: String, new: Boolean) : this(name, plateNo) {
this.new = new
}
}
|
Обратите внимание, что мы не можем объявлять свойства внутри вторичного конструктора, как мы это делали для первичного конструктора. Если мы хотим сделать это, мы должны объявить это внутри тела класса, а затем инициализировать его во вторичном конструкторе.
В приведенном выше коде мы устанавливаем значение по умолчанию для new
свойства для класса Car
(помните, что new
не является ключевым словом в Kotlin) — тогда мы можем использовать вспомогательный конструктор, чтобы изменить его, если мы захотим. В Kotlin каждый вторичный конструктор должен вызывать первичный конструктор или вызывать другой вторичный конструктор, который вызывает первичный конструктор — для этого мы используем ключевое слово this
.
Также обратите внимание, что внутри класса может быть несколько вторичных конструкторов.
01
02
03
04
05
06
07
08
09
10
11
12
13
|
class Car(val name: String, val plateNo: String) {
var new: Boolean?
var colour: String = «»
constructor(name: String, plateNo: String, new: Boolean) : this(name, plateNo) {
this.new = new
}
constructor(name: String, plateNo: String, new: Boolean, colour: String ) :
this(name, plateNo, new) {
this.colour = colour
}
}
|
Если класс расширяет суперкласс, то мы можем использовать ключевое слово super
(аналогично Java) для вызова конструктора суперкласса (мы обсудим наследование в Kotlin в следующем посте).
1
2
3
4
5
6
|
// directly calls primary constructor
val car1 = Car(«Peugeot 504», «XYZ234»)
// directly calls 1st sec.
val car2 = Car(«Peugeot 504», «XYZ234», false)
// directly calls last sec.
val car3 = Car(«Peugeot 504», «XYZ234», false, «grey»)
|
Как я уже говорил ранее, для того, чтобы мы явно включили модификатор видимости в конструктор в классе, мы должны включить ключевое слово constructor
— по умолчанию конструкторы являются общедоступными.
1
2
|
class Car private constructor(val name: String, val plateNo: String) {
//…
|
Здесь мы сделали конструктор закрытым — это означает, что пользователи не могут создавать экземпляры объекта, используя его конструктор напрямую. Это может быть полезно, если вы хотите, чтобы пользователи вместо этого вызывали другой метод (фабричный метод) для косвенного создания объектов.
2. Любой и Ничего Типов
В Kotlin самый верхний тип в иерархии типов называется Any
. Это эквивалентно типу Object
Java. Это означает, что все классы в Kotlin явно наследуются от типа Any
, включая String
, Int
, Double
и так далее. Тип Any
содержит три метода: equals
, toString
и hashcode
.
Мы также можем использовать класс Nothing
в Kotlin в функциях, которые всегда возвращают исключение, другими словами, для функций, которые не завершаются нормально. Когда функция возвращает Nothing
, мы знаем, что она выдаст исключение. В Java не существует эквивалентного типа такого рода.
1
2
3
|
fun throwException(): Nothing {
throw Exception(«Exception message)
}
|
Это может пригодиться при тестировании поведения обработки ошибок в ваших модульных тестах.
3. Модификаторы видимости
Модификаторы видимости помогают нам ограничить доступность нашего API для публики. Мы можем предоставить различные модификаторы видимости для наших классов, интерфейсов, объектов, методов или свойств. Kotlin предоставляет нам четыре модификатора видимости:
общественного
Это значение по умолчанию, и любой класс, функция, свойство, интерфейс или объект, имеющий этот модификатор, может быть доступен из любого места.
Частный
Функция, интерфейс или класс верхнего уровня, объявленные как private
могут быть доступны только в одном и том же файле.
Любая функция или свойство, которые объявлены private
внутри класса, объекта или интерфейса, могут быть видны только другим членам этого же класса, объекта или интерфейса.
1
2
3
|
class Account {
private val amount: Double = 0.0
}
|
защищенный
protected
модификатор можно применять только к свойствам или функциям внутри класса, объекта или интерфейса — его нельзя применять к функциям, классам или интерфейсам верхнего уровня. Свойства или функции с этим модификатором доступны только в пределах класса, определяющего его, и любого подкласса.
внутренний
В проекте, в котором есть модуль (модуль Gradle или Maven), класс, объект, интерфейс или функция, указанные с помощью internal
модификатора, объявленного внутри этого модуля, доступны только из этого модуля.
1
2
3
|
internal class Account {
val amount: Double = 0.0
}
|
4. Смарт Кастинг
Приведение означает получение объекта другого типа и преобразование его в другой тип объекта. Например, в Java мы используем оператор instanceof
чтобы определить, принадлежит ли конкретный тип объекта другому типу, прежде чем мы затем приведем его.
1
2
3
4
5
|
/* Java */
if (shape instanceof Circle) {
Circle circle = (Circle) shape;
circle.calCircumference(3.5);
}
|
Как вы можете видеть, мы проверили, является ли экземпляр shape
Circle
, и затем мы должны явно привести ссылку на shape
к типу Circle
чтобы мы могли вызывать методы типа circle
.
Еще одна удивительная вещь о Kotlin — это умение его компилятора, когда дело доходит до кастинга. Давайте теперь посмотрим версию в Kotlin.
1
2
3
4
|
/* Kotlin */
if (shape is Circle) {
shape.calCircumference(3.5)
}
|
Довольно аккуратно! Компилятору полезно знать, что блок if
будет выполняться только в том случае, если объект shape
является экземпляром Circle
поэтому механизм приведения выполняется для нас под капотом. Теперь мы можем легко вызывать свойства или функции типа Circle
внутри блока if
.
1
2
3
|
if (shape is Circle && shape.hasRadius()) {
println(«Circle radius is {shape.radius}»)
}
|
Здесь последнее условие после &&
в заголовке if
будет вызываться, только если первое условие true
. Если shape
не является Circle
, то последнее условие не будет оцениваться.
5. Явный Кастинг
Мы можем использовать оператор as
(или небезопасный оператор приведения) для явного приведения ссылки типа к другому типу в Kotlin.
1
2
|
val circle = shape as Circle
circle.calCircumference(4)
|
Если явная операция приведения является недопустимой, обратите внимание, что будет ClassCastException
. Чтобы исключить исключение при приведении, мы можем использовать оператор безопасного приведения (или обнуляемый оператор приведения) as?
,
1
|
val circle: Circle?
|
То as?
Оператор попытается привести к целевому типу и возвращает null
если значение не может быть приведено вместо исключения. Помните, что подобный механизм был обсужден в разделе Nullability в Nullability, Loops, и Условия пост в этой серии. Читайте там для повышения квалификации.
6. Объекты
Объекты в Kotlin больше похожи на объекты JavaScript, чем объекты Java. Обратите внимание, что объект в Kotlin не является экземпляром определенного класса!
Объекты очень похожи на классы. Вот некоторые характеристики объектов в Котлине:
- Они могут иметь свойства, методы и блок
init
. - Эти свойства или методы могут иметь модификаторы видимости.
- У них не может быть конструкторов (первичных или вторичных).
- Они могут расширять другие классы или реализовывать интерфейс.
Давайте теперь углубимся в то, как создать объект.
1
2
3
4
5
6
|
object Singleton {
fun myFunc(): Unit {
// do something
}
}
|
Мы object
ключевое слово object
перед именем объекта, который мы хотим создать. Фактически, мы создаем синглтоны, когда создаем объекты в Kotlin, используя конструкцию object
, потому что существует только один экземпляр объекта. Вы узнаете больше об этом, когда мы обсудим совместимость объектов с Java.
Singleton — это шаблон проектирования программного обеспечения, который гарантирует, что у класса есть только один экземпляр, и этот класс обеспечивает глобальную точку доступа к нему. Каждый раз, когда несколько классов или клиентов запрашивают класс, они получают один и тот же экземпляр класса. Вы можете проверить мой пост о шаблоне синглтона в Java, чтобы узнать больше об этом.
Вы можете получить доступ к объекту или одиночному объекту в любом месте вашего проекта, если вы импортируете его пакет.
1
|
Singleton.myFunc()
|
Если вы Java-кодер, мы обычно создаем синглтоны:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
public class Singleton {
private static Singleton INSTANCE = null;
// other instance variables can be here
private Singleton() {};
public static synchronized Singleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return(INSTANCE);
}
// other instance methods can follow
}
|
Как видите, использование object
конструкции Kotlin делает ее лаконичной и простой для создания синглетонов.
Объекты в Kotlin могут быть использованы также для создания констант. Обычно в Java мы создаем константы в классе, делая его публичным статическим конечным полем, например:
1
2
3
4
5
6
|
public final class APIConstants {
public static final String baseUrl = «https://www.myapi.com/»;
private APIConstants() {}
}
|
Этот код в Java может быть преобразован в Kotlin более кратко, как это:
1
2
3
4
5
|
package com.chike.kotlin.constants
object APIConstants {
val baseUrl: String = «http://www.myapi.com/»
}
|
Здесь мы объявили константы APIConstants
со свойством baseUrl
внутри пакета com.chike.kotlin.constants
. Под капотом для нас создается приватный статический конечный член baseUrl
который инициализируется строкой URL.
Чтобы использовать эту константу в другом пакете в Kotlin, просто импортируйте пакет.
1
2
3
|
import com.chike.kotlin.constants.APIConstants
APIConstants.baseUrl
|
Совместимость с Java
Kotlin преобразует объект в окончательный класс Java под капотом. Этот класс имеет приватное статическое поле INSTANCE
которое содержит один экземпляр (единственный элемент) класса. Следующий код показывает, как просто пользователи могут вызывать объект Kotlin из Java.
1
2
|
/* Java */
Singleton.INSTANCE.myFunc()
|
Здесь был создан Java-класс Singleton
с открытым статическим финальным членом INSTANCE
, включая myFunc()
финальную функцию myFunc()
.
Чтобы сделать объектную функцию или свойство в Kotlin статическим членом сгенерированного Java-класса, мы используем аннотацию @JvmStatic
. Вот как это использовать:
1
2
3
4
5
6
|
object Singleton {
@JvmStatic fun myFunc(): Unit {
// do something
}
}
|
Применяя аннотацию @JvmStatic
к myFunc()
, компилятор превратил ее в статическую функцию.
Теперь Java-вызывающие абоненты могут вызывать его как обычный статический вызов члена. Обратите внимание, что использование статического поля INSTANCE
для вызова участников все равно будет работать.
1
2
|
/* Java */
Singleton.myFunc()
|
7. Сопутствующие объекты
Теперь мы должны понять, какие объекты находятся в Kotlin, давайте погрузимся в другой тип объектов, называемых объектами-компаньонами.
Поскольку Kotlin не поддерживает статические классы, методы или свойства, подобные тем, что есть в Java, команда Kotlin предоставила нам более мощную альтернативу, называемую объектами-компаньонами . Сопутствующий объект — это, по сути, объект, который принадлежит классу — этот класс известен как сопутствующий класс объекта. Это также означает, что характеристики, которые я упомянул для объектов, также применяются к сопутствующим объектам.
Создание объекта-компаньона
Подобно статическим методам в Java, объект-компаньон связан не с экземпляром класса, а с самим классом, например, с фабричным статическим методом, который выполняет задачу создания экземпляра класса.
1
2
3
4
5
6
|
class Person private constructor(var firstName: String, var lastName: String) {
companion object {
fun create(firstName: String, lastName: String): Person = Person(firstName, lastName)
}
}
|
Здесь мы сделали конструктор private
— это означает, что пользователи вне класса не могут создавать экземпляр напрямую. Внутри нашего блока объекта-компаньона у нас есть функция create()
, которая создает объект Person
и возвращает его.
Вызов функции сопутствующего объекта
создание объекта- companion
ленив. Другими словами, он будет создан только тогда, когда это необходимо в первый раз. Создание экземпляра объекта- companion
происходит, когда создается экземпляр класса- companion
или осуществляется доступ к членам объекта- companion
.
Давайте посмотрим, как вызвать функцию сопутствующего объекта в Kotlin.
1
2
|
val person = Person.create(«Cersei», «Lannister»)
println(person.firstName) // prints «Cersei»
|
Как видите, это все равно что вызывать статический метод в Java как обычно. Другими словами, мы просто вызываем класс, а затем вызываем участника. Обратите внимание, что помимо функций мы также можем иметь свойства внутри нашего сопутствующего объекта.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
class Person private constructor(var firstName: String, var lastName: String) {
init {
count++
}
companion object {
var count: Int = 0
fun create(firstName: String, lastName: String): Person = Person(firstName, lastName)
init {
println(«Person companion object created»)
}
}
}
|
Также обратите внимание, что companion
класс имеет неограниченный доступ ко всем свойствам и функциям, объявленным в его сопутствующем объекте, тогда как сопутствующий объект не может получить доступ к членам класса. У нас может быть блок кода init
внутри объекта- companion
— он вызывается сразу при создании объекта-компаньона.
1
2
3
|
Person.create(«Arya», «Stark»)
Person.create(«Daenerys», «Targaryen»)
println(Person.count)
|
Результат выполнения кода выше будет:
1
2
|
Person companion object created
2
|
Помните, что может существовать только один экземпляр объекта- companion
класса.
Мы также можем предоставить объекту-компаньону имя.
1
2
3
4
5
6
|
// …
companion object Factory {
var count: Int = 0
fun create(firstName: String, lastName: String): Person = Person(firstName, lastName)
}
// …
|
Здесь мы дали ему название « Factory
. Затем мы можем назвать это так в Kotlin:
1
|
Person.Factory.create(«Petyr», «Baelish»)
|
Этот стиль многословен, поэтому придерживаться предыдущего способа гораздо предпочтительнее. Но это может пригодиться при вызове сопутствующей объектной функции или свойства из Java.
Как я уже говорил ранее, как и объекты, сопутствующие объекты могут также включать свойства или функции, реализовывать интерфейсы и даже расширять класс.
01
02
03
04
05
06
07
08
09
10
11
12
|
interface PersonFactory {
fun create(firstName: String, lastName: String): Person
}
class Person private constructor(var firstName: String, var lastName: String) {
companion object : PersonFactory {
override fun create(firstName: String, lastName: String): Person {
return Person(firstName, lastName)
}
}
}
|
Здесь у нас есть интерфейс PersonFactory
с единственной функцией create()
. Рассматривая наш новый модифицированный companion
объект, он теперь реализует этот интерфейс (вы узнаете об интерфейсах и наследовании в Kotlin в следующем посте).
Совместимость с Java
Под капотом сопутствующие объекты компилируются аналогично тому, как компилируется объект Kotlin. В нашем собственном случае для нас создаются два класса: конечный класс Person
и внутренний статический конечный класс Person$Companion
.
Класс Person
содержит последний статический член под названием Companion
— это статическое поле является объектом внутреннего класса Person$Companion
. Внутренний класс Person$Companion
также имеет своих собственных членов, и одна из них является публичной конечной функцией с именем create()
.
Обратите внимание, что мы не дали нашему объекту-компаньону имя, поэтому сгенерированный статический внутренний класс был Companion
. Если бы мы дали ему имя, то сгенерированное имя было бы именем, которое мы дали ему в Kotlin.
1
2
|
/* Java */
Person person = Person.Companion.create(«Jon», «Snow»);
|
Здесь объект-компаньон в Kotlin не имеет имени, поэтому мы используем имя Companion
предоставленное компилятором для вызывающих Java-программ, для его вызова.
Аннотация @JvmStatic
применяемая к элементу объекта-компаньона, работает так же, как и для обычного объекта.
Расширения сопутствующих объектов
Аналогично тому, как функции расширения могут расширять функциональность класса, мы также можем расширять функциональность сопутствующего объекта. (Если вы хотите освежить в расширенных функциях в Kotlin, посетите учебник по расширенным функциям в этой серии).
01
02
03
04
05
06
07
08
09
10
11
12
|
class ClassA {
companion object {
}
}
fun ClassA.Companion.extFunc() {
// … do implementation
}
ClassA.extFunc()
|
Здесь мы определили функцию расширения extFunc()
для сопутствующего объекта ClassA.Companion
. Другими словами, extfunc()
является расширением объекта-компаньона. Затем мы можем вызвать расширение, как если бы оно было функцией-членом (это не так!) Объекта-компаньона.
За кулисами компилятор создаст статическую служебную функцию extFunc()
. Объект-получатель в качестве аргумента этой служебной функции — ClassA$Companion
.
Вывод
В этом уроке вы узнали об основных классах и объектах в Kotlin. Мы рассказали о классах:
- создание класса
- конструкторы
- свойства
- модификаторы видимости
- умный кастинг
- явное приведение
Кроме того, вы узнали о том, как объекты и сопутствующие объекты в Kotlin могут легко заменить ваши статические методы, константы и синглтоны, которые вы кодируете в Java. Но это не все! Есть еще больше узнать о занятиях в Котлине. В следующем посте я покажу вам еще больше интересных возможностей, которые есть у Kotlin для объектно-ориентированного программирования. До скорого!
Чтобы узнать больше о языке Kotlin, я рекомендую посетить документацию Kotlin . Или ознакомьтесь с некоторыми другими нашими статьями по разработке приложений для Android здесь, на Envato Tuts +!