Введение в Kotlin: интерфейсы, модификаторы доступа, вложенные классы, ключевые слова this и object
Введение в Kotlin: функции, переменные, условия, циклы
Введение в Kotlin: классы, конструкторы, методы и свойства, наследование
Эта статья является третьей частью в цикле, посвященному введению в главные конструкции языка Kotlin. Сегодня будут рассмотрены интерфейсы, модификаторы доступа, вложенные и внутренние классы, а также ключевые слова this и object. Подробнее о них можно почитать в официальной документации: интерфейсы, модификаторы доступа, вложенные классы, object.
Интерфейсы
Интерфейсы в Kotlin очень похожи на интерфейсы в Java 8. Они могут содержать объявления абстрактных методов и реализации обычных методов.
Интерфейс определяется с помощью ключевого слова interface:
interface MyInterface {
fun bar()
fun foo() {
// optional body
}
}Реализация интерфейсов
Класс или объект может реализовать один или несколько интерфейсов. Синтаксис при реализации такой же, как и при наследовании.
class Child : MyInterface {
fun bar() {
// body
}
}Свойства в интерфейсах
Вы можете объявить свойства в интерфейсе. Свойства объявляются в интерфейсе как абстрактные:
interface MyInterface {
val property: Int // abstract
val propertyWithImplementation: String
get() = "foo"
fun foo() {
print(property)
}
}
class Child : MyInterface {
override val property: Int = 29
}Решение проблем с переопределением
При реализации нескольких интерфейсов могут возникнуть конфликты в именах методов. Например:
interface A {
fun foo() { print("A") }
fun bar()
}
interface B {
fun foo() { print("B") }
fun bar() { print("bar") }
}
class C : A {
override fun bar() { print("bar") }
}
class D : A, B {
override fun foo() {
super<A>.foo()
super<B>.foo()
}
}Интерфейсы А и В и объявляют функции foo() и bar(). Оба интерфейса реализуют функцию foo(), однако функцию bar() реализует только интерфейс B. В классе C мы реализовали метод bar(), т. к. реализация его является обязательной. В классе D мы реализуем оба интерфейса (A и B), в связи с чем возникает конфликт. Метод foo() реализуют оба интерфейса, и компилятор не знает, какой из них выбрать. Мы должны явно указать, какой из методов нужно вызывать.
Модификаторы доступа
Классы, объекты, интерфейсы, конструкторы, функции, свойства и их сеттеры имеют модификаторы доступа (геттеры всегда имеют такую же видимость, как и свойства).
Это модификаторы доступа в Kotlin: private, protected, internal и public. По умолчанию используется модификатор public.
Пакеты
Функции, свойства и классы, объекты и интерфейсы могут быть объявлены как “top-level”, т. е. находиться внутри пакета:
// имя файла: example.kt
package foo
fun baz() {}
class Bar {}— Если вы не укажете модификатор, по умолчанию будет использован модификатор public и все объявления будут доступны во всем пакете.
— Если вы укажете в качестве модификатора private, объявления будут видны только внутри файла, содержащего декларацию.
— Если вы укажете в качестве модификатора internal, объявления будут видны во всем модуле.
— Модификатор protected не доступен для «top-level»-объявлений.
Примеры:
// имя файла: example.kt
package foo
private fun foo() {} // виден внутри файла example.kt
public var bar: Int = 5 // виден везде
private set // сеттер виден только в файле example.kt
internal val baz = 6 // видно внутри модуляКлассы и интерфейсы
При объявлении внутри класса:
- private — объявления видны только внутри этого класса (в том числе для всех его членов);
- protected — так же, как и private, + видимость в подклассах;
- internal — все клиенты внутри этого модуля могут видеть класс;
- public —все клиенты могут видеть класс.
Примечание для программистов Java: внешний класс не видит private-поля своих внутренних классов в Kotlin.
Примеры:
open class Outer {
private val a = 1
protected val b = 2
internal val c = 3
val d = 4 // public по умолчанию
protected class Nested {
public val e: Int = 5
}
}
class Subclass : Outer() {
// a не виден
// b, c и d видны
// Nested и e видны
}
class Unrelated(o: Outer) {
// o.a, o.b не видны
// o.c и o.d видны
// Outer.Nested не виден, и Nested::e не виден
}Конструкторы
Чтобы указать видимость основного конструктора класса, используйте следующий синтаксис (обратите внимание, что вы должны добавить ключевое слово constructor):
class C private constructor(a: Int) { ... }Здесь конструктор является приватным.
Локальные переменные
Локальные переменные, функции и классы не могут иметь модификаторы доступа.
Модули
Модификатор internal означает видимость внутри модуля:
- модуль IntelliJ IDEA (или Android Studio);
- проект Maven или Gradle;
- набор файлов, скомпилированных одним вызовом Ant задачи.
Вложенные классы
Классы могут быть вложенными один в другой:
class Outer {
private val bar: Int = 1
class Nested {
fun foo() = 2
}
}
val demo = Outer.Nested().foo() // == 2Внутренние классы
Вложенный класс может быть помечен как inner для доступа к элементам внешнего класса. Внутренние классы содержат ссылку на объект внешнего класса:
class Outer {
private val bar: Int = 1
inner class Inner {
fun foo() = bar
}
}
val demo = Outer().Inner().foo() // == 1Из примера видно, что класс Inner имеет доступ к переменным класса Outer.
Ключевое слово this
Ключевое слово this используется в классах для обозначения текущего экземпляра класса. Т. е. слово this возвращает объект, в котором оно вызывается.
Пример использования ключевого слова this:
class Human (var age: Integer) {
fun isSimiliarAge(age:Integer): Boolean = this.age == age
}Метод isSimiliarAge проверяет, являются ли два человека ровесниками. Пример выглядит немного глупо, т. к. в метод можно было передать также объект класса Human и брать возраст у него, но ничего более толкового в голову не пришло.
Перечисления enum
Синтаксис создания перечислений в Kotlin очень похож на аналогичный в Java, например:
enum class Direction {
NORTH, SOUTH, WEST, EAST
}Также перечисления в Kotlin могут иметь переопределяемые функции:
enum class ProtocolState {
ERROR {
override fun signal() = "Ошибка загрузки"
},
SUCCESS {
override fun signal() = "Все ок"
};
abstract fun signal(): String
}Обратите внимание, что здесь мы отделили объявление перечислений от объявления функций знаком ; .
Так же как и в Java, мы можем получить константу по имени или же получить список всех констант:
EnumClass.valueOf(value: String): EnumClass EnumClass.values(): Array<EnumClass>
Метод valueOf() может выбросить IllegalArgumentException, если значение не найдено.
Синглтоны и аналоги статических методов
Kotlin не имеет модификатора static, а это значит, что мы не можем создавать статические переменные и методы. Хорошо, а как нам тогда создать Singleton в Kotlin?
К счастью, object может справиться с этим. Ключевое слово companion object используется для доступа к членам конкретного класса (не объекта).
class MyClass {
companion object Factory {
val info = "This is factory"
fun getMoreInfo(): String {
return "This is factory fun"
}
}
}
MyClass.info // This is factory
MyClass.getMoreInfo() // This is factory funКаждая переменная или метод, расположенные внутри companion object, могут вызываться по имени класса (типа статических). И сейчас мы создадим класс-синглтон с использованием паттерна «ленивая загрузка» (аналогичным образом создается класс-синглтон в Java):
class Singleton private constructor() {
init {
println("This is a singleton")
}
private object Holder {
val INSTANCE = Singleton()
}
companion object {
val instance: Singleton by lazy {
Holder.INSTANCE
}
}
var b: String? = null
}- Приватный конструктор используется для того, чтобы гарантировать, что объект данного класса будет создан только внутри этого класса.
- Метод init будет вызван при загрузке данного класса, т. е. при первом вызове Singleton.instance.
- Holder object & lazy instance используются для создания единственного экземпляра класса.
Пример использования:
var first = Singleton.instance // This is a singleton first.b = "hello singleton" var second = Singleton.instance println(second.b) // hello singleton
Но в Kotlin создать Singleton можно гораздо проще! Для этого достаточно только слова object:
object Singleton {
init {
println("This ($this) is a singleton")
}
var b: String? = null
}Пример использования:
var first = Singleton // This is a singleton first.b = "hello singleton" var second = Singleton println(second.b) // hello singleton
На этом третья часть серия статей "Введение в Kotlin" заканчивается. Следующая часть серии доступна здесь.
Источники:
#3 Уроки Kotlin. Введение: интерфейсы
#4 Уроки Kotlin. Введение: модификаторы доступа
#5 Уроки Kotlin. Введение: вложенные классы
#6 Уроки Kotlin. Введение: ключевое слово this