本文旨在通过介绍 Kotlin 的基础知识、开发环境配置、基本语法,使工程师对 Kotlin 形成整体认识,顺利上手 Kotlin 开发;对比 Kotlin 和 Java 的不同用法,Kotlin 和 Java 混合编译相互调用的方法,为工程师开始 Java 代码改造、进行现有 Java 工程下的混合开发做好准备。
Kotlin 简介
编译速度和执行效率至少同 Java 一样,原因包括:
Kotlin 特性:
实用(Pragmatic);
简洁(Concise);
安全(Safe)。
版本演进
2010 年:Kotlin 进入开发;
2011 年 7 月:推出 Kotlin;
2016.2.15:官方发布第一个 release 版本 v1.0;
2017.3.2:Kotlin v1.1 release 版本;
2017.5.19:Google 正式宣布官方支持 Kotlin,将 Kotlin 作为 Android 开发一级编程语言(First-Class Language)。
目标人群:
Kotlin 开发环境配置及代码运行
环境配置
IDE
o IntelliJ IDEA
§ IDE 15 及以上已经集成支持 Kotlin;
§ 15 以下安装 Kotlin 插件:Preferences–>Pugins–>搜索 Kotlin
o Android Studio
§ 添加 Kotlin 语言库和序列化对象 Parcelable 插件:
§ AS 3.0 及以上已经集成;
§ AS 3.0 前,Settings—>Plugins—>InstallJetBrains plugin—>Kotlin
§ 在项目中配置的三个方式:
Tools|Kotlin|ConfigureKotlin
打开一个 Java file,通过以下三种方式让 Java 转 Kotlin(不可以从 Kotlin 转 Java),首次操作并会提示配置:
Code—>Convert Javafile to Kotlin file(ALT⇧⌘K)
Help—>Find Action(⇧⌘A):输入 Convert Java file to Kotlin file
.java 中的代码片段拷贝到 .kt 后即自动转换。
新建 Kotlin flie 提示配置。
§ 配置生效后的效果:
在 build.gradle(Project)中会自动添加如下:
buildscript{
ext.kotlin_version = '1.1.2-5'
dependencies {
classpath"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
复制代码
在 build.gradle(Module)中添加如下:
applyplugin: 'kotlin-android'
dependencies{
compile"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
}
复制代码
Eclipse
o 安装插件:Help–> EclipseMarketplace…–> 搜索 Kotlin
· Online playground
o https://try.kotl.in/
o 无需安装、配置,可编写、编译、运行。
· 支持 Maven/Ant/Gradle
· 支持 cmd line:
// Compile
kotlinc<source file or directory> -include-runtime -d <jar name>
// Executecode
java -jar<jar name>
复制代码
代码运行
· IntelliJ
o New Project :选择 Kotlin(JVM)
o 在 src 下 New Kotlin File/Class:MyTest
o 点击左侧图标运行
并会自动完成下图配置。
特别注意:config 是 MyTestKt,而不是 MyTest,自行 Edit Config 时,在 Main Class 栏位不要填错。
Kotlin 语法
本节列出 Kotlin 的基本语法,尤其是和 Java 写法不同之处。
1.变量(Variables)
用关键字"val" 声明常量,对应 java 中的"final";用关键字"var" 声明变量,对应 Java 中的非"final"。Kotlin 支持类型推断,省略变量类型。
valanswer: Int = 42
/ / 类型推断,可以省略类型:
val answer= 42
复制代码
2.函数(Functions)
Kotlin 对于函数定义和 Java 区别较大, Kotlin 中一个函数的构成如下图:
有块函数体(blockbody function)和表达式函数体(expression body function)两种形式。
// blockbody function
fun max(a:Int, b: Int): Int
{
return if (a > b) a else b
}
//expression body function
fun max(a:Int, b: Int): Int = if (a > b) a else b
复制代码
包级别函数(top-levelfunction / Package-level function)
不属于任何类,属于当前包(package),其功能类似于 Java 中提供静态方法的工具类。
// Java 中的静态工具类:
packagestrings;
publicclass JoinKt {
public static String joinToString(...) {... }
}
复制代码
// Kotlin 中使用包级别函数:
packagestrings
funjoinToString(...): String { ...
Kotlin 的包级别函数在 Java 中的调用方式:
// 在 Kotlin 中 package 前加入注释
@file:JvmName("StringFunctions")
packagestrings
funjoinToString(...): String { ... }
复制代码
// 在 Java 中调用:
importstrings.StringFunctions;
StringFunctions.joinToString(list,", ", "", "");
复制代码
扩展函数(extension function)
在类外定义,但是是被扩展类的类成员。其格式如下:
其中 receiver type 是被新增的函数所在的类。receiver object 是调用此方法的对象。
Java 没有的特性。
举例:在 StringUtil.kt 文件中,扩展 String 类的一个方法,名为 lastChar(),用于返回字符串的最后一个字符。
packagestrings
funString.lastChar(): Char = get(length - 1)
lastChar() 这个方法在 Kotlin 和 Java 中都可以被使用:
// Kotlin 中调用:
println(“Kotlin”.lastChar())
// Java 中调用:
char c =StringUtilKt.lastChar(“Kotlin”);
在 Android 中,为 Framework 类定义扩展函数,可以简化系统方法的调用方式。
举例:为 Activity 扩展 toast 方法,在派生 Activity 中直接调用 toast() 取代 Toast.makeTest().show()。
//Kotlin
fun Activity.toast(message:CharSequence, duration: Int = Toast.LENGTH_SHORT) {
Toast.makeText(this, message,duration).show()
复制代码
}
高阶函数
可以接受函数作为参数,也可以返回函数作为结果。Java 没有的特性。举例:Android 中使用 SharePreference 来存储数据,需要包括 edit()、appply()等方法。可以使用 Kotlin 封装成一个传入函数参数的方法,直接调用。如下:
//kotlin
funSharedPreferences.editor(f: (SharedPreferences.Editor) -> Unit) {
val editor = edit()
f(editor)
editor.apply()
}
//实际调用
PreferenceManager.getDefaultSharedPreferences(this).editor{
it.putBoolean("installed", true)
}
复制代码
1.#### 控制结构(Control)
when
比 Java 中的 switch 强大,条件支持类型更宽松。
// 一对一,其中 Color 是封装类,不是简单类型。
fungetMnemonic(color: Color) =
when(color) {
RED -> "Richard"
ORANGE -> "Of"
YELLOW -> "York"
GREEN -> "Gave"
BLUE -> "Battle"
INDIGO -> "In"
VIOLET -> "Vain"
}
// 多对一
fungetWarmth(color: Color) = when(color)
{
RED, ORANGE, YELLOW -> "warm"
GREEN -> "neutral"
BLUE, INDIGO, VIOLET -> "cold"
}
// 混合
funmix(c1: Color, c2: Color) = when (setOf(c1, c2)) {
setOf(RED, YELLOW) -> ORANGE
setOf(YELLOW, BLUE) -> GREEN
setOf(BLUE, VIOLET) -> INDIGO
else -> throw Exception("Dirtycolor")
}
复制代码
循环
while 和 Java 写法相同。for 写法如下:
range 是范围,可以有以下几种写法:
1…10 // 表示 [1,10]
1 until 10// 表示 [1, 10)
100 downTo1 step 2 //表示 100, 98,96…2
1. #### 集合(collection)
提供新 API,比 Java 的定义方式简单。
val set =setOf(1, 7, 53)
val list =listOf(1, 7, 53)
val map =mapOf(1 to “one”, 7 to “seven”, 53 to"fifty-three")
1. #### Interface
与 Java 相比, Kotlin 一个显著的改进之处是:接口支持提供缺省实现的方法,派生类不再用实现所有接口方法。同时 Kotlin 中,复写方法必须显示声明 override,否则不被编译。
interfaceClickable {
fun click()
// 可以有缺省实现的接口函数
fun showOff() = println("I'mclickable!")
}
interfaceFocusable {
fun showOff() = println("I'mfocusable!")
}
//override 必须写上
classButton : Clickable, Focusable {
override fun click() = println("I wasclicked")
// Clickable, Focusable 都有 showOff(),所以此处必须复写 showOff(),否则编译错误。反之有缺省实现的函数可以不复写。
override fun showOff() {
// java 的写法是:Clickable.super.showOff()
super<Clickable>.showOff()
super<Focusable>.showOff()
}
}
复制代码
1. #### 类
Java 中类和方法默认是可继承的( open),Kotlin 中默认是不可继承的( final )。这是因为:基类的修改易造成派生类的写法不符合之前的预期,破坏了稳定性。
// Button 可以被继承
open classButton : Clickable {
// final
fun disable() {}
// open
open fun animate() {}
//继承自接口,也继承了其open属性。
override fun click() {}
//显示声明为final,将改变其open属性。
final override fun click() {}
}
复制代码
抽象类
Kotlin 中,抽象函数必须被派生类实现,默认为 open,非抽象函数默认为 final,需要被继承要标记 open。
//抽象类
abstractclass Animated {
abstract fun animate()
open fun stopAnimating() { }
fun animateTwice() { }
}
复制代码
类的构造函数
Kotlin 与 Java 构造函数区别较大。 Kotlin 包括主构造函数和次构造函数。
(1) 主构造函数(primary constructor)在类名后直接用 () 或 constructor() 定义参数,个数为一个或没有。在构造函数中为属性设置默认值,解决了 Java 多构造函数。
// 在构造函数中声明的参数,它们默认属于类的公有字段,可以直接被其他类访问到,如果不想被访问到,要用 private 修饰。
// valnickname
classUser(val nickname: String)
classUser(private val nickname: String)
classSecretive private constructor() {}
// 有默认值的构造函数。解决 Java 的多构造函数。
classUser(val nickname: String,
valisSubscribed: Boolean = true)
// 使用方式:
val user1= User("alice")
val user2= User("aclice", flase)
复制代码
(2) 次构造函数(Secondary constructor):在{}中定义,个数为没有、一个或多个。可用于和 Java 互调。
classMyButton : View {
//this继承自自己类的构造函数
constructor(ctx: Context): this(ctx,MY_STYLE) {
}
//super使用父类的构造函数
constructor(ctx: Context, attr:AttributeSet): super(ctx, attr) {
}
}
复制代码
Data Class
Kotlin 特有,不用 IDE 生成模板代码,而是编译成 class 过程中插入 toString()(实例所有变量的 String 表示)、equals()(实例所有变量的值比较)、hasCode()(实例的 HashCode)等方法。无需显式 override
data classUser(val nickname: String,
val isSubscribed: Boolean = true)
val user1= User("Bob", false)
val user2= User("Bob", false)
println(user1)
println(user1.equals(user2))
// 结果为:
User(nickname=Bob,isSubscribed=false)
true
// 如果是普通class,且没有重写toString() 等方法,结果为:
User@78308db1
false
复制代码
可以将 dataClass 的.kt 编译成.class,再反编译成.java,看它被自动插入了的方法。
1. #### object 关键字
Kotlin 特有,在定义类、属性、方法、块的同时创建出一个实例 INSTANCE,而无需调用构造函数,所以不用为 object 类定义构造函数。
object Payroll{
val allEmployees =arrayListOf<Person>()
fun calculateSalary() {
for (person in allEmployees) {
...
}
}
}
// 直接用类名调用
Payroll.allEmployees.add(Person(...))
Payroll.calculateSalary()
复制代码
可用于 Java 中静态方法改造为 Kotlin 方法后,用 Java 调用 Kotlin 中方法:
// Kotlin 改造的静态方法
objectStringUtils {
@JvmStatic fun isEmpty(str: String):Boolean {
return "" == str
}
fun isEmpty2(str: String): Boolean {
return "" == str
}
}
// 在 Java 中两种调用方法:
// 为了让 Java 调用,使用@JvmStatic
StringUtils.isEmpty("hello");
// objectclass,获取实例再调用。
StringUtils.INSTANCE.isEmpty2("hello");
复制代码
介绍语法过程中,涉及到了 Kotlin 文件结构,这里总结一下:
· package
o package 用于在命名空间组织代码,而非用于可见性;
o package 内多个文件间的方法可以直接调用到;
o package 间用 import。
· class, function, property
o 一个 .kt file 可以包含多个 class;
o 包级别函数(top-level function)和包级别属性(top-level property)不属于任何类。
Kotlin 与 Java 相比的特性
本节列出 Kotlin 的基本语法,尤其是和 Java 写法不同之处。
前文讲到一些 Kotlin 较 Java 相比的特性,包括支持类型推断简化变量定义方式,支持扩展函数方便方法调用,支持缺省参数简化多构造函数或多复写,支持缺省函数使接口继承不用重写所有方法等,再介绍几个:
空指针检测 NullPointerException
通过编译报错,预防潜在空指针;通过安全访问方式代替之前的 != null 的繁琐使用方式。
var s1:String? = "abc"
if(s1!!.length == 1)
if(s1?.length == 1)
复制代码
类型转换异常检测 ClassCastException
关键字 is 用于类型检测及转换。类型不匹配不执行 if 语句;类型匹配直接使用,无需显式强转。
if (s1 isString) {
println(s1.toUpperCase())
}
复制代码
lateinit
通常,类的成员变量声明时必须初始化,否则编译错误。使用 lateinit 可以将初始化延迟到使用时。
private lateinit var textview: TextView
复制代码
Kotlin 与 Java 混合编程
除了前面章节示例中的互相调用,这里再列举一些互相调用的例子。
1. Kotlin 调用 Java
如果 Java 类存在 setXXX 和 getXXX 的方法,Kotlin 即可以直接调用其属性。
//Java
publicclass DataClass {
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}}
// Kotlin
funmain(args: Array<String>) {
val dataClass = DataClass()
dataClass.id = 0
println(dataClass.id)
}
复制代码
Java 方法名用的是 Kotlin 关键字时,需要反引号``包起来从能被 Kotlin 调用。
Kotlin 在访问 Java 变量或 Java 方法时,变量、方法的参数和返回值(称为:平台类型 Platform Type)的空与否由我们自己决定,编译不会报错。所以在 Java 类中可以用 @NotNull、@Nullable 做好提示。
//Java
publicclass DataClass {
private String id;
public String getId() {
return null;
}
}
//Kotlin
funmain(args: Array<String>) {
val nullSafetyJava = DataClass()
val data = nullSafetyJava.id
//编译不报错,但是运行时dataCannotBeNull这行报错。
val dataCanBeNull: String? = data
val dataCannotBeNull: String = data
}
//
// Java
publicabstract class NullSafetyAbsClass {
public abstract @NotNull StringformatDate(Date date);}
//开发者在Kotlin中使用时,根据@NotNull注释就需要做好保护。
classNullSafetySubClass : NullSafetyAbsClass(){
override fun formatDate(date: Date?):String? {
return date?.toString()
}
}
funmain(args: Array<String>) {
val nullSafetySubClass =NullSafetySubClass()
val formattedDate: String? =nullSafetySubClass.formatDate(Date())
println(formattedDate)
}
复制代码
2. Java 调用 Kotlin
Kotlin 自动生成属性对应的 set、get 方法(val:生成 getxxx(),var: 生成 getxxx()和 setxxx()),isxxx 属性生成 isxxx()和 setxxx(),不仅适用于 boolean,同样适用于任何类型。
class User(val nickname: String, val isOpen: Boolean)
//Java中调用
User user = new User("abc",true);
public void getProperty() {
user.getNickname();
user.isOpen();
}
复制代码
Java 调用 Kotlin 有默认值参数的函数,需传入完整的实参列表。但是如果 Kotlin 对函数增加 @JvmOverloads 注释,编译后的 class 会自动加入其它参数个数的构造函数, Java 就可以根据所需使用相应参数的构造函数。
//Kotlin
classOverloads {
fun overloaded(a: Int, b: Int = 0, c: Int =1){
}
}
//Java 中使用:
newOverloads().overloaded(0, 0, 1);
//Kotlin进行改写,加入 @JvmOverloads
classOverloads{
@JvmOverloads
fun overloaded(a: Int, b: Int = 0, c: Int =1) {
}
}
复制代码
Kotlin 与 Java 混合编程
介绍一个好用的第三方库。
Anko
辅助 Android 开发的第三方库,使开发者可以忽略 Java 版本对 Android SDK 的限制。其主要模块和功能包括:
· Anko Commons:封装 Intents、Dialog 和 toasts、Logging、Resources and dimensions 的方法;
· Anko Layouts:支持 DSL(domainspecific languages),让 Android 布局写法更简单、相同逻辑可复用、避免了 xml 渲染为对象的消耗;
· Anko SQLite:用链式调用支持数据库访问;
· Anko Coroutines:基于 kotlinx.coroutines 库。
Anko 源码及使用方法可参考:https://github.com/Kotlin/anko
配置方法:
dependencies{
compile"org.jetbrains.anko:anko:$anko_version"
}
//按需引入
dependencies{
// Anko Commons
compile"org.jetbrains.anko:anko-commons:$anko_version"
// Anko Layouts
compile"org.jetbrains.anko:anko-sdk25:$anko_version" // sdk15, sdk19, sdk21,sdk23 are also available
compile"org.jetbrains.anko:anko-appcompat-v7:$anko_version"
// Coroutine listeners for Anko Layouts
compile "org.jetbrains.anko:anko-sdk25-coroutines:$anko_version"
compile"org.jetbrains.anko:anko-appcompat-v7-coroutines:$anko_version"
// Anko SQLite
compile"org.jetbrains.anko:anko-sqlite:$anko_version"
}
}
复制代码
Anko Layouts 举个例子:
verticalLayout{
val email= editText
{ hint ="Email" }
valpassword = editText { hint = "Password"
transformationMethod =PasswordTransformationMethod.getInstance()
}
button("LogIn")
{ onClick {
logIn(email.text,password.text) }
} }
复制代码
用 DSL 定义布局,抽取公共逻辑,笔者认为可以用于布局模板化开发。
Kotlin 更多特性及实践,后继继续分享给大家。
本文转载自公众号贝壳产品技术(ID:gh_9afeb423f390)。
原文链接:
https://mp.weixin.qq.com/s/T5Q7lH50RuR6ISCjFotpDg
评论