【从零开始撸一个App】Kotlin

工欲善其事必先利其器。像我们从零开始撸一个App的话,选择最合适的语言是首要任务。如果你跟我一样对Java蹒跚的步态和僵硬的语法颇感无奈,那么Kotlin在很大程度上不会令你失望。虽然为了符合JVM规范和兼容Java,它引入了一些较为复杂的概念和语法,很多同学就是因此放弃入门。其实越深入进去,就会越欲罢不能。除了Android开发,博主也常在后端使用Kotlin编码,有时因为某些原因同时使用Java混编。总的来说,能减少代码量,提高生产效率,似乎代码结构也更清晰了。如果你没有Kotlin的经验,但是比较过Java和C#,你就明白我的意思了,甚至Kotlin有些地方比C#还方便。可以说Kotlin既有C#便捷的语法,亦背靠Java平台良好的生态,那么你还在犹豫什么?

基础

var:可变,是一个可变变量。可知var类型属性不能设置为延迟加载属性,因为在lazy中并没有setValue(…)方法。在DI场景下,常与lateinit搭配使用,可参看Kotlin中lateinit变量在字节码层面上的解释
val:不可变,一个只读变量。另外还有const val,只允许在top-level级别和object中使用。它们的区别如下:

const val 可见性为public final static,可以直接访问。

val 可见性为private final static,并且val 会生成方法getNormalObject(),通过方法调用访问。

Unit:当一个函数没有返回值的时候,我们用Unit来表示这个特征,同Java中的void。

open:在java中允许创建任意的子类并重写方法任意的方法,除非显示的使用了final关键字进行标注。而在Kotlin的世界里面则不是这样,在Kotlin中它所有的类默认都是final的,那么就意味着不能被继承,而且在类中所有的方法也是默认是final的,那么就是Kotlin的方法默认也不能被重写。为类增加open,class就可以被继承了;为方法增加open,那么方法就可以被重写了。

inline:Kotlin 内联函数 inline。它会将代码块拷贝到调用的地方,减少了调用层数和额外对象的产生。
crossinline:这是因inline的副作用而引入的关键字。由于inline会将代码拷贝到调用的地方,如果代码里面有return,那么目标代码(调用者)的逻辑可能就被破坏了。用crossinline修饰相应的lambda,将return返回到对应标签[,而不是返回到整个方法]。
reified:为了应对Java伪泛型导致的代码冗余问题。可参看使用Kotlin Reified 让泛型更简单安全。这主要是应对Java中的泛型擦除。Java中的泛型是伪泛型,即它的泛型只存在于编译期,在生成的字节码文件中是不包含任何泛型信息的(不过至少在编译期就能及早发现类型不匹配的问题),在编译后的字节码文件中,就已经被替换为原来的原始类型(Raw Type/Object)了,并且在相应的地方插入了强制转型代码,是为类型擦除。因此对于运行期的Java语言来说,ArrayList<int>与ArrayList<String>就是同一个类型。相对的,C#中使用的泛型,就是真泛型,其泛型无论在程序源码中、编译后的IL中或是运行期的CLR中都是切实存在的,List<int>与List<String>就是两个不同的类型,它们有自己的虚方法表和类型数据。
下面是我封装RabbitMQ消费端监听的代码(感兴趣的同学可以参看本人博文RabbitMQ入门指南获取更多信息):

/** * 从指定队列获取消息,并定义回调(for kotlin) * * @param queue the name of the queue from where receive messages * @param block callback when a message arrived */ inline fun <reified T> receive(queue: String, crossinline block: (T) -> Boolean) { factory.newConnection().use { val conn = it.get() val channel = conn.createChannel() channel.basicConsume(queue, false, object : DefaultConsumer(channel) { override fun handleDelivery(consumerTag: String?, envelope: Envelope, properties: AMQP.BasicProperties?, body: ByteArray) { try { val message = JSON.parseObject(String(body), object : TypeReference<T>() {}) val done = block(message) if (done) { channel.basicAck(envelope.deliveryTag, false) } else { //若失败则重新投递一次,否则丢弃或投递到死信队列(若配置了的话) channel.basicNack(envelope.deliveryTag, false, !envelope.isRedeliver) } } catch (e: Exception) { _logger.error("处理消息-${String(body)}时发生错误-${e.message}") throw e } } }) } }

注意Java编译器不支持inline和reified等关键字,所以如果要使用Java调用,还需要另外写for java的版本。

field:用于属性取值/赋值逻辑(如果显式定义的话),类似于C#属性中的value关键字,防止访问器的自递归而导致程序崩溃的 StackOverflowError异常,参看kotlin学习—Field

this@ClassName:匿名内部类对象引用[包含它的]外部对象。

by:修饰属性和字段,提供若干效用,可参看Kotlin by。
还可以在类定义时使用,可以将某实例的所有的 public 方法委托该类[,似乎这些方法就是在这个类中定义的]。这应该是组合的形态,但我们也可用它实现某种语法程度的“多继承”,以后面协程部分的代码片段为例:

class BasicCorotineActivity : AppCompatActivity(), CoroutineScope by MainScope() {}

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpjpyp.html