Kotlin速通

原文地址

原文链接

前言

默认有Java基础,或者其他静态语言基础。本文主要用于速通和温习,不会设计过于细节用法,并且主要针对服务端的开发,关于多平台的开发参考本站《Kotlin多平台开发流程》

基础

  • 在服务端开发时,可以和Java完全兼容开发,就和TsJs一样可以混用,可以同时使用两者的库包

  • 服务端Spring框架脚手架项目创建可以使用Spring Start

  • 分号可以省略

  • 注释和大多数静态语言相同

  • 构建类对象不需要new关键字,直接调用构造函数即可

  • 函数和对象可以写在类外,作为包级方法/对象被公共调用

  • 基本内置类型:ByteShortIntLongFloatDoubleUByteUShortUIntULongBooleanCharStringArrayBoolean/Byte/Char/Double/Float/Double/Int/Long/ShortArray,使用和长度和其他静态语言基本相同

  • 变量声明类型后置val a : Int,如果可以根据右侧表达式推断类型,则可以不声明val b = 1,使用valvar修饰符区分是否为只读,只读变量只能赋值一次

  • Unit类型某种程度代表void,可以省略

  • Kotlin支持字符串模板可以在字符串中直接使用表达式,val s2 = "${s1.replace("is", "was")}, but now is $a"

  • 同样,函数返回值和参数的声明格式为fun sum(a: Int, b: Int): Int { return a + b },可以简写为表达式fun sum(a: Int, b: Int) = a + b

  • 函数和C++一样支持默认值参数,同时在调用时候支持命名调用

    fun printMessageWithPrefix(message: String, prefix: String = "Info") = println("[$prefix] $message")
    fun main() {
        printMessageWithPrefix("Hello", "Log")                             
        printMessageWithPrefix("Hello")                                    
        printMessageWithPrefix(prefix = "Log", message = "Hello")  
    }
    
  • 支持本地函数,即函数内声明函数,本地函数可以直接使用外部函数内的变量

    fun dfs(graph: Graph) {
        val visited = HashSet<Vertex>()
        fun dfs(current: Vertex) {
            if (!visited.add(current)) return
            for (v in current.neighbors)
                dfs(v)
        }
        dfs(graph.vertices[0])
    }
    
  • 默认所有方法不能被重写,如果需要重写,使用open修饰被重写的方法,override修饰重写方法

  • 默认所有类不能被继承,如果需要继承类,使用open修饰父类open class Parent,继承写法同C++

  • 类属性不仅可以在类体中,还可以直接写在声明中,声明中的变量即为默认构造函数所需要的变量,没有类声明则默认构造函数为无参构造函数,类似C++中的构造函数的初始化列表

    class Rectangle(var height: Double, var length: Double) {
        var perimeter = (height + length) * 2
    }
    
  • 条件语句的基本写法和其他静态语言相同,但是可以综合三目表达式进行简写fun maxOf(a: Int, b: Int) = if (a > b) a else b

  • 循环语句格式也基本相同,支持fordo-whilewhile,但是for循环迭代修饰符需要改成迭代关键字

    for (item in items) {
        println(item)
    }
    for (index in items.indices) {
        println("item at $index is ${items[index]}")
    }
    
  • switch改为when,实际执行上更加类似于if-elseif-else的简写,而不是switch

    fun describe(obj: Any) =
        when (obj) {
            1          -> "One"
            "Hello"    -> "Greeting"
            is Long    -> "Long"
            !is String -> "Not a string"
            else       -> "Unknown"
        }
    
  • 集合Collections包含ListSetMap,又根据是否可修改叠加修饰Mutable,使用in进行存在判断或者迭代,和Java一样支持各种流操作。Map有两种取值方法使用[]或者getValue,区别在于对于不存在的键返回null还是NoSuchElementException,可以在创建时使用withDefault自定义默认值,就可以让getValue达到和[]相同的效果或者特定的实现

    for (item in items) {
        println(item)
    }
    if(item in items) {
        println("exist")
    }
    val fruits = listOf("banana", "avocado", "apple", "kiwifruit")
    fruits
        .filter { it.startsWith("a") }
        .sortedBy { it }
        .map { it.uppercase() }
        .forEach { println(it) }
    
  • Kotlin支持Int/CharRangeRange数据结构,用于存储离散数据数据,可以使用1..10'a'..'z'进行初始化,使用in进行条件判断或者正反迭代,迭代时可以自定义步长

    if (x in 1..10) {
        println("in range")
    }
    for (x in 1..5) {
        print(x)
    }
    for (x in 1..10 step 2) {
        print(x)
    }
    for (x in 9 downTo 0 step 3) {
        print(x)
    }
    
  • 为了防止空指针,所有类都可以跟一个?修饰符,表达该对象是可以为null的,该种对象在没有进行显示/隐式的空判断就调用内部函数时,编译器就会报错,从而避免运行时的空指针,?.表达不为null时调用的表达式,而?:表达为空时执行的表达式

    //val a : Int = null  //complie fail
    var b : Int? = null //compile pass
    val a : Int
    //a = null //compile fail
    val ret = b?.toString() ?: "null" //compile pass
    
  • 自动类型转换,Kotlin中的is对应Java中的instanceof,但是在判断成功后Kotlin会将该种类型转为为相对应的类,减少编码成本。同样在进行NULL判断后,带有?修饰符的类型对象也会自动转化为不会为NULL的普通类型对象

    fun getStringLength(obj: Any): Int? {
        if (obj is String) {
            return obj.length
        }
        return null
    }
    fun deleteNull(a : String?) : String = a ?: "empty"
    

拓展

  • 泛型的写法基本和其他静态语言差不多

    class MutableStack<E>(vararg items: E) {   
      private val elements = items.toMutableList()
        
      fun push(element: E) = elements.add(element)       
      fun peek(): E = elements.last()                     
      fun pop(): E = elements.removeAt(elements.size - 1)
      fun isEmpty() = elements.isEmpty()
      fun size() = elements.size
    
      override fun toString() = "MutableStack(${elements.joinToString()})"
    }
    
    fun <E> mutableStackOf(vararg elements: E) = MutableStack(*elements)
    
  • 运算符重载,熟悉C++的小伙伴肯定不陌生,在Kotlin我可以通过Operation FunctionInfix Function和实现类似功能。对于运算符重载,只有特定的关键字可以被运算符替代,比如times*div/等,更多对应关系参考Operator overloading

    //operation
    fun main() {
        operator fun Int.times(str: String) = str.repeat(this)
        println(2 * "Bye ")      
    }
    //infix
    fun main() {
      infix fun Int.mytimes(str: String) = str.repeat(this)    
      println(2 mytimes "Bye ")                                    
    }
    

    Infix Function则可以随意指定合理的函数名称,由于Kotlin在类属性写法上没有publicprivate等范围限制,也不需要像C++一样通过友元来突破访问限制,本质上还是外部的函数调用,比如对上例而言,实际上调用的是fun mytimes(this: Int, str: String) = str.repeat(this)

    在不涉及到继承重写等情况下,这种重载可以将我们想要的方法新增到无法修改的第三方包的类中,拥有类似作用的还有KotlinExtension Functions and Properties机制,用于新增方法和属性到指定类中

    data class Item(val name: String, val price: Float) 
    data class Order(val items: Collection<Item>)  
    
    fun Order.maxPricedItemValue(): Float = this.items.maxByOrNull { it.price }?.price ?: 0F 
    fun Order.maxPricedItemName() = this.items.maxByOrNull { it.price }?.name ?: "NO_PRODUCTS"
    val Order.commaDelimitedItemNames: String 
        get() = items.map { it.name }.joinToString()
    
    fun main() {
        val order = Order(listOf(Item("Bread", 25.0F), Item("Wine", 29.0F), Item("Water", 12.0F)))
        println("Max priced item name: ${order.maxPricedItemName()}")
        println("Max priced item value: ${order.maxPricedItemValue()}")
        println("Items: ${order.commaDelimitedItemNames}")
    }
    

    但由于本身不是真正修改了类所以在继承等情况不会按照预期运行

    fun main() {
        open class Shape
        class Rectangle: Shape()
        fun Shape.getName() = "Shape"
        fun Rectangle.getName() = "Rectangle"
        fun printClassName(s: Shape) {
            println(s.getName())
        } 
        printClassName(Rectangle()) //Shape
    }
    

    在上例中实际调用的函数为fun getName(this: Shape) = "Shape",并不会表现出多态性,了解更多Extensions

  • Koltin函数可变长度参数,使用vararg关键字

    fun printAllWithPrefix(vararg messages: String, prefix: String) { 
        for (m in messages) println(prefix + m)
    }
    printAllWithPrefix(
        "Hello", "Hallo", "Salut", "Hola", "你好",
        prefix = "Greeting: "                                         
    )
    

    传入后message的类型可以认为是Array<T>,如果需要作为可变长度参数作为另一个函数的参数则需要增加修饰符*,例如fun printAllWithPrefix(vararg messages: String, prefix: String) = printAllWithPrefixOther(*messages, prefix = prefix)

  • Kotlin包含四种特殊类data classenum classsealed classobject

    • data class方便创建数据类,将数据属性放到初始化列表中,该种类会自动生成copyget等方法

      data class User(val name: String, val id: Int) {           // 1
          override fun equals(other: Any?) =
              other is User && other.id == this.id               // 2
      }
      
    • enum class枚举类和其他语言基本类似,

      enum class Color(val rgb: Int) {  
          RED(0xFF0000),               
          GREEN(0x00FF00),
          BLUE(0x0000FF),
          YELLOW(0xFFFF00);
          fun containsRed() = (this.rgb and 0xFF0000 != 0)
      }
      
    • sealed class,该类可以继承,但是只能被同包下其他的类继承,不能被不同包的类继承

    • object,懒汉单例模式的简单写法,即使在多线程下,该种类只会被实例化一次

      object DoAuth { 
          fun takeParams(username: String, password: String)  = println(" parameters = $username:$password")
      }
      fun main(){
          DoAuth.takeParams("foo", "qwerty") 
      }
      

      object的使用场景一般还有匿名对象和静态函数

      • 匿名对象

        val helloWorld = object {
            val hello = "Hello"
            val world = "World"
            override fun toString() = "$hello $world"
        }
        print(helloWorld)
        
      • 静态函数,但是官方文档更建议使用包级方法代替这种静态方法写法

        class BigBen { 
            companion object Bonger {     
                fun getBongs(nTimes: Int) {      
                    for (i in 1 .. nTimes) {
                        print("BONG ")
                    }
                }
            }
        }
        fun main() {
            BigBen.getBongs(12)   
        }
        
  • Kotlin同样支持Lambda表达式,可以通过类型推断和当前对象it来简略表达式

    val upperCase1: (String) -> String = { str: String -> str.uppercase() } // pass
    val upperCase2: (String) -> String = { str -> str.uppercase() }         // pass
    val upperCase3 = { str: String -> str.uppercase() }                     // pass
    // val upperCase4 = { str -> str.uppercase() }                          // error, the compiler has no chance to infer the type
    val upperCase5: (String) -> String = { it.uppercase() }                 // pass
    val upperCase6: (String) -> String = String::uppercase                  // pass
    
  • 熟悉C++的小伙伴肯定了解,除了使用类继承实现多态,我们还可以使用函数指针来实现多态。Kotlin同样支持函数指针的实现

    //函数作为参数
    fun sum(x: Int, y: Int) = x + y        
    fun calculate(x: Int, y: Int, operation: (Int, Int) -> Int): Int {
        return operation(x, y)      
    }
    fun main() {
        val sumResult = calculate(4, 5, ::sum)             
        val mulResult = calculate(4, 5) { a, b -> a * b }      
    }
    //函数作为返回值
    fun square(x: Int) = x * x    
    fun operation(): (Int) -> Int { 
        return ::square
    }
    fun main() {
        val func = operation()
        println(func(2))    
    }
    
  • Destructuring Declarations,如果你只需要一个对象中的某个成员,那么使用析构声明的语法可以简化代码结构,更多使用参考Destructuring Declarations

    val (x, y, z) = arrayOf(5, 10, 15) 
    val (min, max) = findMinMax(listOf(100, 90, 50, 98, 76, 83))
    
    data class User(val username: String, val email: String)
    val (_, emailAddress) = User("Mary", "mary@somewhere.com")
    

常用

集合处理

只读集合ListSetMap,可写集合MutableListMutableSetMutableSet,初始化方法以List举例有listofmutableListofemptyList等,底层数据结构和其他语言基本类似。注意,如果将可变集合转为不可变集合时,由于指针相同,此时改变可变集合,不可变集合也会改变

  • 元素正向过滤filter

    val numbers = listOf(1, -2, 3, -4, 5, -6)
    val positives = numbers.filter { x -> x > 0 }
    val negatives = numbers.filter { it < 0 } 
    
    val strings = listOf("1", "2", " ", "")
    println(strings.filter(String::isNotBlank))
    println(strings.filter(String::isNotEmpty))
    
  • 元素映射map

    val numbers = listOf(1, -2, 3, -4, 5, -6)
    val doubled = numbers.map { x -> x * 2 }
    val tripled = numbers.map { it * 3 }
    
  • 元素存在性查询anyallnone

    val numbers = listOf(1, -2, 3, -4, 5, -6)  
    numbers.any { it < 0 }
    numbers.all { it < 6 }  
    numbers.none { it > 6 }
    
  • 元素获取findfindLastfirstfirstOrNulllastlastOrNullminminOrNullmaxmaxOrNullgetOrElse

    val words = listOf("Lets", "find", "something", "in", "collection", "somehow") 
    
    val findFirst = words.find { it.startsWith("some") }  //something
    val findFirst2 = words.find { it.startsWith("somexx") }  //null
    val findLast = words.findLast { it.startsWith("some") } //somehow
    
    val first0 = words.first() //Lets
    val first = words.first { it.startsWith("some") }  //something
    //val first2 = words.first { it.startsWith("somexx") }  // NoSuchElementException
    val first3 = words.firstOrNull{ it.startsWith("somexx") }  //null
    val last0 = words.last() //somehow
    val last = words.last { it.startsWith("some") } //somehow
    
    val list = listOf(0, 10, 20)
    println(list.getOrElse(1) { 42 }) //10
    println(list.getOrElse(10) { 42 }) //42
    val map = mutableMapOf<String, Int?>()
    println(map.getOrElse("x") { 1 }) //1
    
  • 元素统计count,支持条件统计

    val numbers = listOf(1, -2, 3, -4, 5, -6)
    val totalCount = numbers.count() 
    val evenCount = numbers.count { it % 2 == 0 } 
    
  • 元素排序sortsortedDescending,支持条件排序

    val shuffled = listOf(5, 4, 2, 1, 3, -10) 
    val natural = shuffled.sorted()  
    val descendingBy = shuffled.sortedByDescending { abs(it)  }
    
  • 集合类型转化,同属性去重associateBy,同属性聚合groupBy,分割partition,嵌套列表平铺flatMap,双列表同索引组合zip

    data class Person(val name: String, val city: String, val phone: String)
    val people = listOf(
        Person("John", "Boston", "+1-888-123456"),
        Person("Sarah", "Munich", "+49-777-789123"),
        Person("Svyatoslav", "Saint-Petersburg", "+7-999-456789"),
        Person("Vasilisa", "Saint-Petersburg", "+7-999-123456"))
    val phoneBook = people.associateBy { it.phone } // Map<phone, person>
    val cityBook = people.associateBy(Person::phone, Person::city) // Map<phone, city>
    val peopleCities = people.groupBy(Person::city, Person::name) // Map<city, List<name>>
    
    val numbers = listOf(1, -2, 3, -4, 5, -6)
    val evenOdd = numbers.partition { it % 2 == 0 }  
    val (positives, negatives) = numbers.partition { it > 0 }
    
    val fruitsBag = listOf("apple","orange","banana","grapes")
    val clothesBag = listOf("shirts","pants","jeans")
    val cart = listOf(fruitsBag, clothesBag) // List<List<String>>
    val mapBag = cart.map { it } // List<List<String>>
    val flatMapBag = cart.flatMap { it }  // List<String>
    
    val A = listOf("a", "b", "c")    
    val B = listOf(1, 2, 3, 4)
    val resultPairs = A zip B // List<Pairs> size=3
    val resultReduce = A.zip(B) { a, b -> "$a$b" } // List<String> size=3
    

匿名函数

类型对象使用返回值使用场景
let使用对象调用(块内自定义该对象名称,或者使用默认的it最后一个表达式以外部函数为主的计算
run使用对象调用(块内方法默认都是由该对象发起,即this最后一个表达式以成员函数为主的计算
with对象作为参数(块内方法默认都是由该对象发起,即thisUnit成员函数为主的非计算逻辑
apply使用对象调用(块内方法默认都是由该对象发起,即this返回自身对象属性初始化
also使用对象调用(块内自定义该对象名称,或者使用默认的it返回自身涉及外部函数的对象初始化
val empty = "test".let {
    print(it)
    it.isEmpty()
}

val len = "test".run { 
    println("\tis empty? " + isEmpty()) 
    println("\tlength = $length")   
    length                             
}

with(configuration) {
    println("$host:$port")
}

val jake = Person() 
val stringDescription = jake.apply {  
    name = "Jake"    
    age = 30
    about = "Android developer"
}.toString()    

val jake2 = Person("Jake", 30, "Android developer")
    .also { 
        writeCreationLog(it) 
    }

代理模式

Kotlin对于代理模式的使用,有进一步的简化。使用代理模式可以在不干涉原本继承体系情况对类方法进行增加和拓展,遵从合成复用原则,更多的使用组合解决问题,减少耦合提高可拓展性。首先我们回顾下Java中代理模式的写法:

    interface Animal {
        void roar();
    }

    class Tiger implements Animal {
        @Override
        public void roar() {
            System.out.print("owu");
        }
    }

    class Bear implements Animal {
        @Override
        public void roar() {
            System.out.print("aoo");
        }
    }

    class ZooAnimal implements Animal {
        private Animal animal;

        ZooAnimal(Animal animal) {
            this.animal = animal;
        }

        @Override
        public void roar() {
            animal.roar();
            System.out.println(", I want to leave");
        }
    }

    @Test
    void test() {
        new ZooAnimal(new Bear()).roar();
        new ZooAnimal(new Tiger()).roar();
    }

Kotlin中,支持部分方法增强,不需要覆写所有方法(默认和by标识的对象实现相同),增强类ZooAnimal的写法一般为:

class ZooAnimal(val animal: Animal) : Animal by animal {
    override fun roar() {
        animal.roar()
        println(", I want to leave")
    }
}
  • 代理属性方法

    import kotlin.reflect.KProperty
    class Example {
        var p: String by Delegate() 
        override fun toString() = "Example Class"
    }
    class Delegate() {
        operator fun getValue(thisRef: Any?, prop: KProperty<*>): String {    
            return "$thisRef, thank you for delegating '${prop.name}' to me!"
        }
        operator fun setValue(thisRef: Any?, prop: KProperty<*>, value: String) { 
            println("$value has been assigned to ${prop.name} in $thisRef")
        }
    }
    fun main() {
        val e = Example()
        println(e.p) //Example Class, thank you for delegating 'p' to me!
        e.p = "NEW" //NEW has been assigned to p in Example Class
    }
    

    既然我们可以直接代理属性方法,也就是意味着我们可以通过这种代理是实现类属性对象的单例创建。这些常用的代理已经被内置到Kotlin标准库当中了,使用lazy实现,blockingLazy支持多线程下的单例属性创建

    class LazySample {
        init {
          println("created!") 
        }   
        val lazyStr: String by lazy {
            print("computed! ") 
            "data"
        }
    }
    fun main() {
        val sample = LazySample() // created!
        println("${sample.lazyStr}") // computed! data
        println("${sample.lazyStr}") // data
    }
    

    类似的,我们可以通过Map初始化对象,在使用MutableMap下,该映射图甚至可以跟随对象var属性的变化而变化

    class User(val map: Map<String, Any?>) {
        val name: String by map        
        val age: Int     by map     
    }
    fun main() {
        val user = User(mapOf(
                "name" to "John Doe",
                "age"  to 25
        ))
    }
    

打印日志

pirnt("this is something")
pirntln("this is something")

或者,导入包

        <dependency>
            <groupId>io.github.oshai</groupId>
            <artifactId>kotlin-logging-jvm</artifactId>
            <version>${version}</version>
        </dependency>
private val log = KotlinLogging.logger {}

@Component
class SomeService { 
    fun output() {
        val time = System.currentTimeMillis()
        log.info { "[op:SomeService:output] cur time = $time" }
    }
}

原文地址

原文链接

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值