热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Kotlin如何成为我们Android开发的主要语言

转载至:Kotlin如何成为我们Android开发的主要语言引言Kotlin是一个基于JVM的新的编程语言,由JetBrains开发。JetBrains,作为目前广受欢迎的JavaIDE

转载至:Kotlin如何成为我们Android开发的主要语言

引言

Kotlin是一个基于JVM的新的编程语言,由JetBrains开发。JetBrains,作为目前广受欢迎的Java IDE IntelliJ的提供商,在Apache许可下已经开源其Kotlin编程语言。与Java相比,Kotlin的语法更简洁、更具表达性,而且提供了更多的特性,比如,高阶函数、操作符重载、字符串模板。它与Java高度可互操作,可以同时用在一个项目中。

Kotlin的定位非常有特点,它并不像Scala那样另起炉灶,Scala是一切尽量自己来,将类库,尤其是集合类都自己来了一遍。实在不够用了再用java的;而Kotlin是对现有Java的增强,尽量用Java的,不够用了再扩展,尤其体现在二者的容器库上,但同时始终保持对java的兼容。这种特点导致Kotlin的学习曲线极低。这是Kotlin官网首页重点强调的:“100% interoperable with Java™”。这意味着什么呢?或者换个问法:我什么时候可以开始在我的项目中引入Kotlin呢?我的回答是:现在就可以视你对kotlin的掌握程度,逐步引入kotlin的代码。

Dima Kovalenko在博客中分享了他们团队使用Kotlin开发商业应用程序的心得和经验,并提供了一些参考资料。希望本文能对广大Android开发程序员有所启发。

几个月前,我们的团队决定开始新的尝试:完全应用Kotlin编程语言开发一个商业应用程序,这是JetBrains公司设计并开源的一种新编程语言。以前,我们有过Kotlin的经验,但那只是小规模应用:将应用程序的一部分转换到一种新的语言,或者应用在花里胡哨的项目。然而,用新的编程语言来开发商业应用程序,我们遇到了一些困难:

  • 我们深深扎根于基于Java的Android开发。切换到Kotlin相当困难,对于以前没有函数式编程经验的人员而言,尤为困难。

  • 有些东西只是不工作。 Dagger也没有立即很好的使用。

所有这些问题,都可能会导致项目无法按期交付、并带来应用程序的稳定性问题。

一个人应该有强烈的转型动力。我们的激励是:相信Kotlin将是Android平台开发的颠覆者——并且这是对我们有挑战的趣事。

让我们打开Kotlin的参考书,开始开发Voter应用程序。Kotlin是一种与Java具有100%互操作性的JVM语言,如果您熟悉Java,那么学习Kotlin就会很容易。然而,如果想充分利用这个编程语言,理解函数式编程概念是至关重要的。

学习函数式编程需要一段时间。所以要有耐心。

至少在学习之初,函数式编程并不容易。我强烈建议使用Martin Ordersky的“Scala中的函数式编程”的课程来学习。Scala有时势不可挡,但它提供了一个极好的函数式编程思维的概述。你可以把Kotlin看成Scala的一个更简化的版本。

为什么我们转向Kotlin阵营

函数式编程风格

Kotlin与Java是100%可互操作的。此外,Kotlin是一种函数式语言。后者允许将表达性的代码编写得更优雅。

纯函数

纯函数(没有副作用的函数)是最重要的函数概念,它允许我们大大降低代码复杂性并消除大多数可变状态。

在Javascript、Java和C#这些命令式编程语言中,副作用无处不在。这使得调试非常困难,因为变量可以在程序中的任何位置更改。所以当出现一个错误时,由于变量可以在错误的时间更改为错误的值,那么你到哪里去寻找错误呢?到处寻找错误吗?这可不好玩啊!

请注意我们是如何操作数据而不更改其内容的。

fun flatTree(tree: TreeNode): List 
= listOf(tree, *tree.children.flatMap(::flatTree).toTypedArray())

高阶函数

高阶函数将函数用作参数,返回函数或将函数作为返回值的函数。

高阶函数无处不在。你只需将函数传递给集合,就能使代码更容易阅读。比如, titles.map {it.toUpperCase()}读取简单的英语,是不是很棒?

让我们设想一种情况,假设要计算不同类型的未读消息的数量。典型的方法是:

  private fun getUnreadCountFromUsers() {
val cOnversations= datasource.getConversations()
var count = 0
for (conversation in conversations) {
if (conversation.recipientId != null) {
for (message in conversation.messages) {
if (message.unread) {
count += 1
}
}
}
}
}

private fun getNumberOfUnreadAttachmentsInGroupConversations() {
val cOnversations= datasource.getConversations()
var count = 0
for (conversation in conversations) {
if (conversation.groupId != null) {
for (message in conversation.messages) {
if (message.unread && message.type == MessageType.ATTACHMENT) {
count += 1
}
}
}
}
}

正如你所看到的,当引入新的需求时,代码变得难以理解、不可收拾。让我们看看如何使用高阶函数来解决这个问题:

private fun getNumberOfAttachmentsInGroupConvesationsFun() {
return getCount({conv -> conv.groupId != null}, {it -> it.type == MessageType.ATTACHMENT && it.unread})
}

private fun getUnreadCountFromUsersFun() {
return getCount({conv -> conv.recipientId != null}, {message -> message.unread})
}

private fun getTotalNumberOfMessages() = getCount({true}, {true})

private fun getCount(convFilter: (Conversation) -> Boolean, messageFilter: (Message) -> Boolean) {
datasource.getConversations()
.filter(convFilter)
.flatMap { it.messages }
.filter(messageFilter)
.fold(0, { count, message -> count + 1})
}

我们还可以想象一下用例,假设想将fold函数变量参数化。比方说,计算未读消息的乘积。

使用高阶函数的另一个例子是用简单的高阶函数代替多个监听器:

BillingView : LinearLayout {
var billingChangeListener: (() -> Unit)? = null
...
}
... // in an activity far, far away
billingView.billingChangeListener { updateUI() }

不变性

不变性使得代码更容易编写,使用和推理代码(类不变性一次建立,然后不变——一劳永逸)。应用程序组件的内部状态将更加一致。Kotlin通过引入val关键字以及Kotlin集合来强制不变性,Kotlin集合在默认情况下是不可变的。 一旦val或者一个集合被初始化,你就可以确定它的有效性。(有关val关键字的更精确的定义,请参阅文末的更新)。

data class Address(val line1: String, val city: String)
val items = listOf(Address("242 5th St", "Los Angeles"), Address("Dovzhenka St. 5", "Kiev"))

Null-safety

这个语言特性使我们仔细考虑了模型类中字段的可空性。以前,当不确定DTO中的字段是否已初始化时,@Nullable和@NotNull的注释就能提供帮助,但也很有限。现在,使用Kotlin,就能让你准确知道什么字段可以为null,什么字段被初始化(例如,Dagger注入的字段),并且你可以对这些字段有严格的控制。结果?几乎没有NullPointerExceptions。(在内部我们管?.叫做“鹅”操作符,因为它看起来像一个鹅的脖子。)

brand?.let { badge.enabled = brand.isNewBadge }
// Can also be written as
badge.enabled = brand?.isNewBadge?:false

Anko

Anko DSL是一个很了不起的的库,它大大简化了工作视图、线程和Android生命周期。据Github的描述,Anko是“令人愉快的Android应用程序开发”,事实证明,的确如此。

selector(items = listOf("Like", "Dislike") {
when (it) {
0 -> if (!liked) likePost()
else -> if (!disLiked) disLikePost()
}
}

doAsync {
// Long background task
uiThread {
alert(R.string.could_not_log_in) {
yesButton { dismiss() }
cancellable = false
}.show()
}
}

注意,当uiThread在Activity内调用时,如果isFinishing为true,块将不会执行。我们实际上不使用这个功能,因为RxJava会处理应用程序中的所有线程,但它是一个很好的功能。

使用Anko而不是XML。虽然Anko还没有做好准备取代标准的Android UI构建,但有的时候,它非常方便。

verticalLayout() {

friendsPanel = friendsPanel.with(friendsData).lparams(width = matchParent)

politicalMapCardView {
setMarker(quizManager.getMarker())
}.lparams(width = matchParent) { topMargin = dip(10) }

cardView() {
verticalLayout() {
topPadding = dip(5)
textView(getString(R.string.register_question))
blueButtonView(text="Register here") {
onClick { browse("https://www.uptech.team") }
}
}
}.lparams(width = matchParent) {
topMargin = dip(10)
bottomMargin = dip(20)
}
}

如您所见,Anko DSL允许您在Android内置视图中使用自定义视图。这一点与标准XML相比有很大的优势。

Kotlin Android扩展:删除ButterKnife依赖

@Bind(R.id.first_name)
protected EditText firstName;

@Bind(R.id.last_name)
protected EditText lastName;

@Bind(R.id.address_line1)
protected EditText addressLine1;

@Bind(R.id.address_line2)
protected EditText addressLine2;

@Bind(R.id.zip_code)
protected EditText zipCode;

@Bind(R.id.state)
protected TextView state;

@Bind(R.id.state_spinner)
protected HintSpinner stateSpinner;

@Bind(R.id.city)
protected EditText city;

@Bind(R.id.frag_shipping_address_save_btn)
protected Button saveBtn;

@Bind(R.id.agreement)
protected TextView agreement;

@Bind(R.id.email)
protected EditText email;

@Bind(R.id.password)
protected EditText password;

@Bind(R.id.create_account_container)
protected LinearLayout accountContainer;

@Bind(R.id.member_container)
protected LinearLayout memberContainer;

@Bind(R.id.logged_in_title)
protected TextView loggedInTitle;

@Bind(R.id.user_email)
protected TextView userEmail;

@Bind(R.id.sign_out)
protected TextView signOut;

@Bind(R.id.scrollview)
protected ScrollView scrollView;

@Bind(R.id.dummy)
protected EditText dummyView;

上面那段代码读起来无聊吗?我敢打赌你一直滚动而没有阅读。在Kotlin,你并不需要任何这些东西。您可以通过其@id XML参数引用视图属性,这些属性将与XML文件中声明的名称相同。更多信息可以在官方文档中找到。

其他整洁的功能

1. 扩展功能和构建器

items = StoreInfo().apply { storeItems = fetchItems() }.let { manager.process(it) }
container.apply {
removeAllViews()
items.forEach { addView(ShopItemView(context).withData(it)) }
}

fun ShopItemView.withData(item: StoreItem): ShopItemView {
title = item.title
image = item.image
Brand.findById(item.id)?.let { brandName = it.name }
}
apply、let和扩展功能可以轻松地用于创建简洁的构建器。

2. 为初学者快速破解

在最初的前几天,你经常被一个问题难倒:你不知道如何在Kotlin中写一个相当简单的Java表达式。这里有一个简单的诀窍,就是是在Java中编写一段代码,然后将其粘贴到Kotlin文件中。感谢JetBrains的工程师们,它会自动转换为Kotlin。

黑客的工作方式就像一个魔术!

3. 摆脱不必要的依赖

Kotlin替换了许多第三方库,如ButterKnife、Google Autovalue、Retrolambda、Lombok和一些RxJava代码。

总结

作为一个软件开发团队,我们面临的主要挑战是提供优秀的产品,并有效地完成工作。虽说开始用Kotlin有效的开发软件,需要你有函数式编程的背景,但是,投入精力去学习是值得的,能给你巨大的回报。我相信,Kotlin是常规Android开发的一个重大改进,能让我们及时提供错误更少的、更加优秀的应用程序。

更新:val实际上并不意味着“不可变的”,而是“只读”。有关详细信息,请参阅下面的参考文章。

参考文献

《Kotlin 参考手册》

《所以,你要成为一名函数式程序员》

《为什么函数式编程很重要》

《Java使用不可变对象编程的6大好处》

《Anko DSL对Android XML-First》

《将应用转换为纯Kotlin的经验教训》

《结果:应用程序投票选举:99.8%无故障用户》

《Kotlin:val不意味着不可变,而意味着只读》


推荐阅读
  • 本文整理了315道Python基础题目及答案,帮助读者检验学习成果。文章介绍了学习Python的途径、Python与其他编程语言的对比、解释型和编译型编程语言的简述、Python解释器的种类和特点、位和字节的关系、以及至少5个PEP8规范。对于想要检验自己学习成果的读者,这些题目将是一个不错的选择。请注意,答案在视频中,本文不提供答案。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 闭包一直是Java社区中争论不断的话题,很多语言都支持闭包这个语言特性,闭包定义了一个依赖于外部环境的自由变量的函数,这个函数能够访问外部环境的变量。本文以JavaScript的一个闭包为例,介绍了闭包的定义和特性。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 本文介绍了RPC框架Thrift的安装环境变量配置与第一个实例,讲解了RPC的概念以及如何解决跨语言、c++客户端、web服务端、远程调用等需求。Thrift开发方便上手快,性能和稳定性也不错,适合初学者学习和使用。 ... [详细]
  • 本文探讨了C语言中指针的应用与价值,指针在C语言中具有灵活性和可变性,通过指针可以操作系统内存和控制外部I/O端口。文章介绍了指针变量和指针的指向变量的含义和用法,以及判断变量数据类型和指向变量或成员变量的类型的方法。还讨论了指针访问数组元素和下标法数组元素的等价关系,以及指针作为函数参数可以改变主调函数变量的值的特点。此外,文章还提到了指针在动态存储分配、链表创建和相关操作中的应用,以及类成员指针与外部变量的区分方法。通过本文的阐述,读者可以更好地理解和应用C语言中的指针。 ... [详细]
  • 重入锁(ReentrantLock)学习及实现原理
    本文介绍了重入锁(ReentrantLock)的学习及实现原理。在学习synchronized的基础上,重入锁提供了更多的灵活性和功能。文章详细介绍了重入锁的特性、使用方法和实现原理,并提供了类图和测试代码供读者参考。重入锁支持重入和公平与非公平两种实现方式,通过对比和分析,读者可以更好地理解和应用重入锁。 ... [详细]
  • 开源Keras Faster RCNN模型介绍及代码结构解析
    本文介绍了开源Keras Faster RCNN模型的环境需求和代码结构,包括FasterRCNN源码解析、RPN与classifier定义、data_generators.py文件的功能以及损失计算。同时提供了该模型的开源地址和安装所需的库。 ... [详细]
  • 用Vue实现的Demo商品管理效果图及实现代码
    本文介绍了一个使用Vue实现的Demo商品管理的效果图及实现代码。 ... [详细]
  • 大数据Hadoop生态(20)MapReduce框架原理OutputFormat的开发笔记
    本文介绍了大数据Hadoop生态(20)MapReduce框架原理OutputFormat的开发笔记,包括outputFormat接口实现类、自定义outputFormat步骤和案例。案例中将包含nty的日志输出到nty.log文件,其他日志输出到other.log文件。同时提供了一些相关网址供参考。 ... [详细]
  • 本文总结了Java中日期格式化的常用方法,并给出了示例代码。通过使用SimpleDateFormat类和jstl fmt标签库,可以实现日期的格式化和显示。在页面中添加相应的标签库引用后,可以使用不同的日期格式化样式来显示当前年份和月份。该文提供了详细的代码示例和说明。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • C# 7.0 新特性:基于Tuple的“多”返回值方法
    本文介绍了C# 7.0中基于Tuple的“多”返回值方法的使用。通过对C# 6.0及更早版本的做法进行回顾,提出了问题:如何使一个方法可返回多个返回值。然后详细介绍了C# 7.0中使用Tuple的写法,并给出了示例代码。最后,总结了该新特性的优点。 ... [详细]
author-avatar
天云2_776
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有