Skip to content

Latest commit

 

History

History
583 lines (429 loc) · 18.1 KB

README.md

File metadata and controls

583 lines (429 loc) · 18.1 KB

KotlinLsns

Курс по дисциплине "Разработка оконных приложений", с использованием ЯП Kotlin и библиотеки JavaFX

Краткис справочник по Kotlin

Более подробно тут:

kotlinlang.ru

metanit.com/kotlin

  1. Основы
  2. Функции
  3. ООП
  4. Null безопасность

Основы

Hello world

fun main(args: Array<String>){
    println("Hello Kotlin")
}

Базовые типы данных

Byte, Short, Int, Long, Float, Double - всё довольно стандартно

Стоит отметить тип any

var name: Any = "42"
name = 42

Any - базовый тип данных

val и var

val a: Int = 42;

var b = 0.0;

a = -42 //error
b = 42.0

val - неизменяемая переменная

var - изменяемая

Условный оператор

if(a == b)
    println("a == b")
else
    println("a != b")

может работать как тернарный оператор

val c = if (a > b){
    a
} else {
    b
}

Аналог switch

val a = 10
when(a){
    10 -> println("a = 10")
    20 -> println("a = 20")
    else -> println("неопределенное значение")
}

так же может работать как аналог тернарного оператора + может быть не только с константами и ренджами

val rate = when(sum){
    in 100..999 -> 10
    in 1000..9999 -> 15
    else -> 20
}

Циклы

for(n in 1..9){
    print("${n} \t")
}

for - может быть только через итераторы

В принципе можно спокойно написать свой For

inline fun <T> For(it : Iterator<T>, cb : (T) -> Unit) {
    while (it.hasNext()) cb(it.next())
}

fun main() {
    For(list.iterator()) { 
        println(it)
    }
}

Синтаксис позволяет вынести определение лямбды за скобку.

while - аналогичен c++

while(i > 0){
    println(i*i)
    i--;
}

Последовательности

var range = 1..10
range =  "a".."z"
range =  10 downTo 0
range = 10 downTo 0 step 2

if (1 in range) {
    print("Yey")
}

Массивы

val numbers: Array<Int> = arrayOf(1, 2, 3, 4, 5)

val numbers2 = Array(3, {5}) // [5, 5, 5]

//2D array
val table: Array<Array<Int>> = Array(3, { Array(5, {0}) })


val arr: Array<String> = arrayOf("1", "2", "3")
for(elem in arr){
    println(elem)
}

Функции

fun funName(param1: type1, param2: type2, ...) : returnType {
    code
}

Аргументы по умолчанию

fun displayUser(name: String, age: Int = 18, position: String="unemployed"){
    println("Name: $name   Age: $age  Position: $position")
}

displayUser(age=21, name="Alice")

Переменное количество аргументов

vararg - ключевое слово, для передачи переменного количества параметров одного типа

fun sum(vararg numbers: Int){
    var result=0
    for(n in numbers)
        result += n
    println("Сумма чисел равна $result")
}

Если параметров много, то vararg - должен быть последним

(Это можно нарушить, если после vararg-параметра идут еще какие-нибудь параметры, то при вызове функции значения этим параметрам передаются через именованные аргументы)

Оператор * (оператор распаковки)

Помагает распаковать массив для передачи его как параметр vararg

fun printUserGroup(group: String, vararg users: String, count:Int){
    println("Count: $count")
    for(user in users)
        println(user)
}
fun main(args: Array<String>) {
 
    val users = arrayOf("Tom", "Bob", "Alice")
    printUserGroup("MO-011", *users, count=3)
}

Возвращение результата из функции

Возвращение значение из функции осуществляется с помощью оператора return

fun fun42() : Int{
    return 42
}

Забавный факт, если функция ничего не возвращает, то ее тип возвращаемого значения - Uint

Однострочные функции

Однострочные функции (single expression function) используют сокращенный синтаксис определения функции в виде одного выражения. Эта форма позволяет опустить возвращаемый тип и оператор return.

fun pow2(x: Int) : Int = x * x

Локальные функции

fun check(num: Int): Boolean{
    fun pow2(num: Int) = num*num
    return pow2(num) > 100
}

Перегрузка функций

fun add(a: Int, b: Int) : Int{
    return a + b
}
fun add(a: Double, b: Double) : Double{
    return a + b
}

Важно в kotlin при перегрузке не учитывает возвращаемый результат функции

Лямбды

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

val printer = {message: String -> println(message)}
val summer = {a: Int, b: Int -> a+b}

val summerAndPrinter = {x:Int, y:Int ->
    val result = x + y
    println("$x + $y = $result")
    result
}

Анонимные функции

Анонимные функции выглядят как обычные за тем исключением, что они не имеют имени.

fun(x: Int): x+x

fun(x: Int, y: Int): Int{ 
    return x + y
}

Зачем нам и лямбды и анонимные функции??

fun loop(list: List<Element>): Boolean {
     list.forEach { element ->
          if (!element.process()) return false   // returns from loop()
     }
     return true
}


fun loop(list: List<Element>): Boolean {
     list.forEach { element ->
          if (!element.process()) return@forEach   // returns from forEach
     }
     return true
}


fun loop(list: List<Element>): Boolean {
     list.forEach(fun(element) {
          if (!element.process()) return false   // returns from forEach
     })
     return true
}

Для чего они нужны? Да чтобы их в функции передавать))

Функции высокого порядка

Функции высокого порядка (high order function) - это функции, которые либо принимают функцию в качестве параметра, либо возвращают функцию, либо и то, и другое.

fun action (n1: Int, n2: Int, operation: (Int, Int)-> Int){
    val result = operation(n1, n2)
    println(result)
}

fun selectAction(key: Int): (Int, Int) -> Int{
    // определение возвращаемого результата
    when(key){
        1 -> return {x:Int, y: Int -> x + y}
        2 -> return {x:Int, y: Int -> x - y}
        3 -> return {x:Int, y: Int -> x * y}
        else -> return  {x:Int, y: Int -> 0}
    }
}

Inline функции

Ключевое слово inline - сообщит компилятору, что мы хотим, чтобы код этой функции подставлялся в месте её вызова.

inline fun pow(i: Int): Int {
    return i*i
}

ООП

Класс создается с использованием ключевого слова class

class ClassName {
    val a = 10
    
    fun getA() : Int{
            return a
    }
}

...

val obj: ClassName = ClassName()

В Kotlin классы могут содержать ряд компонентов:

  • конструкторы и инициализаторы
  • функции
  • свойства
  • вложенные классы
  • объявления объектов

Конструкторы

class Class constructor(param: Int, param2: String) {
    val p1: String
    val p2: Int
    init {
        p1 = param2
        p2 = param
    }
    //Дополнительный конструктор
    constructor(param: Int) : this(param, ""){
        p2 = param
    }

}

Аналог static

class Name {
    companion object {
        @JvmStatic
        fun main(args: Array<String>) {
            launch(GUI1::class.java)
        }
    }
}

Как Вы могли заметить раньше, обчно в качестве точки входа используется fun main(), но можно сделать в стиде java статичную функцию main

В котлин нет статик методов, но есть вспомогательные объекты.

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

  • private: классы, объекты, интерфейсы, а также функции и свойства, определенные вне класса, с этим модификатором видны только в том файле, в котором они определены. Члены класса с этим модификатором видны только в рамках своего класса
  • protected: члены класса с этим модификатором видны в классе, в котором они определены, и в классах-наследниках
  • internal: классы, объекты, интерфейсы, функции, свойства, конструкторы с этим модификатором видны в любой части модуля, в котором они определены. Модуль представляет набор файлов Kotlin, скомпилированных вместе в одну структурную единицу. Это может быть модуль IntelliJ IDEA или проект Maven
  • public: классы, функции, свойства, объекты, интерфейсы с этим модификатором видны в любой части программы. (При этом если функции или классы с этим модификатором определены в другом пакете их все равно нужно импортировать)

По умолчанию всё - public

Наследование и перегрузка методов

Чтобы функциональность класса можно было унаследовать, необходимо определить для этого класса аннотацию open

В kotlin нет множественного наследования!

super - доступ к базовому классу

open class Person(val name: String) {
    open fun display(){
        println("Base")
    }
}
class Employee: Person{
    constructor(name: String) : super(name){
    }
    override fun display() {
        println("Name: $name")
    }
}

Геттеры и Сеттеры

Для каждого свойства можно определять геттер и сеттер.

class Person{
 
    val name: String
        get() {
            return "Name: $name  Age: $age"
        }
        set(value){
            if(value != "")
                field = value
        }
}

Идентификатор field представляет автоматически генерируемое поле, которое непосредственно хранит значение свойства.

Интерфейсы

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

Можно наследоваться от нескольких интерфейсов

interface Movable{
    fun move()      // определение функции без реализации
    fun stop(){     // определение функции с реализацией по умолчанию
        println("Остановка")
    }
}

class Car : Movable{
    override fun move(){
        println("Машина едет")
    }
}

Абстрактные классы

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

val kate: Human     // можем инициализировать классом наследникои
val alice: Human = Human("Alice")   // ошибка

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

Абстрактные классы могут иметь собственную реализацию методов.

Enum

Для моздания enum класса используется ключевое слово enum.

Enum можно наследовать от интерфейсов.

interface Printable{
    fun printName()
}
enum class DayTime: Printable{
    DAY{
        override fun printName(){
            println("День")
        }
    },
    NIGHT{
        override fun printName(){
            println("Ночь")
        }
    }
}

Null безопасность

Одна из ключевых фишек Kotlin - проверка на null во время компиляции.

Ключевое слово null представляет специальный литерал, который указывает, что переменная не имеет как такового значения.

Для присвоения типу null, нужно чтобы этот тип был Nullable, для этого нужно добавить ? к типу.

val a : Int = null //ошибка
val a : Int? = null

Оператор ?:

Позволяет получить альтернативное занченик в случае, если переменная null

var name : String?  = "Tom"
val firstName: String = name ?: "Undefined" // если name = null, то присваивается "Undefined"

Оператор ?.

позволяет объединить проверку значения объекта на null и выполнение функции этого объекта

val name : String?  = "Tom"
val length: Int? = name?.length

Если name - null, то length - null, иначе length = name.length

Оператор !!

Оператор !! (not-null assertion operator) принимает один операнд. Если операнд равен null, то генерируется исключение

val name : String?  = null
val length :Int = name!!.length