Kotlin简介
Kotlin是什么
Kotlin是JetBrains公司开发的一门语言,一圣彼得堡附近的Kotlin岛屿来命名的。Kotlin是运行在JVM上面的一门静态强类型语言,可以编译成JavaScript源码,运行在浏览器上,与Java100%兼容。
Kotlin的特性
为什么有了Java还需要有Kotlin?下面就看看Kotlin的一些特性吧:
- 空类型安全
- Lambda表达式
- 扩展方法
- 类型推导
- 胜任Java能做的所有事情,使用起来比Java简单,例如没有句末分号
Kotlin相关参考资料
https://blog.jetbrains.com/kotlin/
https://github.com/JetBrains/kotlin
Kotlin环境搭建
Kotlin练习环境搭建之--Hello Word!
下面我们先用IDEA来创建一个项目,打开IDEA,创建项目,选择Kotlin(JVM),如下图所示:
Tips:如果没有Kotlin选项,请先安装Kotlin插件,创建项目需要选择lib库。
Tips:首次创建项目需要Index一两分钟。
与Java一样,在src目录下面创建一个Package,创建.kt文件:
fun main(args: Array<out String>) {
print("Hello Word\n")
//创建类的时候不需要new关键字了
print(MyBean(1, "test bean"))
}
//Kotlin风格的数据对象
data class MyBean(var id: Int, var name: String)
那么程序就会输出:
Hello Word
MyBean(id=1, name=test bean)
Kotlin练习环境搭建之--使用Gradle搭建环境
Gradle是一个依赖管理的工具,以前我们都是直接把jar包等源码直接拷贝进来的,但是这样很麻烦。有了Gradle之后,我们就可以通过脚本或者图形化界面进行依赖管理了,对以后依赖库的升级维护也很方便。
同样用IDEA进行开发,下面我们创建一个项目,选择Gradle、Kotlin,如下图所示:
接下来配置构件信息,其中包括代表公司或者组织的GroupId,代表自己在公司或者组织里面的代表自己的ArticleFactId,然后就是构件的版本:
然后就是配置Gradle,这里笔者使用自己本地的Gradle,然后选择生成一些重要的文件夹,例如src/main/java目录:
项目创建好之后如下:
工具已经帮我们创建好需要的目录了。我们下面来看一些与Gradle相关的重要文件:
settings.gradle:是存放每个module的信息的,其内容如下:
rootProject.name = 'KotlinDemo'
build.gradle,与build相关的脚本,其内容如下:
group 'nan.com'
version '1.0-SNAPSHOT'
buildscript {
repositories {
//下面的dependencies中,插件的地址
jcenter()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.0.4"
}
}
//标识Java与Kotlin工程,存放着与编译相关的一系列指令集
apply plugin: 'java'
apply plugin: 'kotlin'
//标识Java的版本,这里没有使用到
sourceCompatibility = 1.5
repositories {
//module中的依赖的地址
jcenter()
}
//Gradle中最重要的功能--依赖管理
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:1.0.4"
testCompile group: 'junit', name: 'junit', version: '4.11'
}
使用Gradle进行依赖管理
由于Kotlin中,反射包是独立与基础包的,因此我们以反射这个包为例子,介绍一下使用Gradle进行依赖管理。
修改build.gradle:
//Gradle中最重要的功能--依赖管理
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:1.0.4"
//增加反射依赖
compile "org.jetbrains.kotlin:kotlin-reflect:1.0.4"
testCompile group: 'junit', name: 'junit', version: '4.11'
}
然后就可以在代码中进行反射操作了,例如我们拿到Person类的构造方法:
fun main(args: Array<String>) {
Person::class.constructors.map {
print(it)
}
//也可以通过代码提示直接改成这种写法
Person::class.constructors.map(::print)
}
data class Person(var id: Int, var name: String) {
}
Kotlin空指针安全
我们先来看一个例子,我们先创建一个Person类:
data class Person(var id: Int, var name: String) {
}
然后在Java代码中new这个类:
public class Test {
public static void main(String[] args) {
//注意第二个参数传了null
Person p = new Person(1, null);
}
}
运行的时候发现报错:
Exception in thread "main" java.lang.IllegalArgumentException: Parameter specified as non-null is null: method Person.<init>, parameter name
at Person.<init>(Test.kt)
at Test.main(Test.java:7)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
因为Kotlin的类中实质上加了@notnull注解。
同样地,我们在Kotlin中使用这个Person,也传个null进去:
fun main(args: Array<String>) {
val p = Person(1, null)
}
你会发现,根本就编译不过。这就是Kotlin的类型安全的体现了,把一些空指针的问题提前到了编译阶段,而不是运行阶段,这一点真的很重要。
Tips:可以通过code -- Conver Java File to Kotlin File或者直接粘贴,把Java代码转换Kotlin代码。
如果你的Person类中的name可以传null的话,就需要加上"?"标识符,完整的示例如下:
fun main(args: Array<String>) {
//不会报错了
val p = Person(1, null)
}
data class Person(var id: Int, var name: String?) {
}
Kotlin中的集合
Kotlin中的集合
与Java中用[]代表集合不一样,在Kotlin中用Array<T>表示集合,例如:
fun main(args: Array<String>) {
}
与Java类似,可以在泛型中指定类型应该继承哪个类型,Java中用extends关键字,在Kotlin中用out关键字,例如:
fun main(args: Array<out Bean>) {
}
集合的基本遍历
下面是Kotlin中集合遍历的集中方法:
fun main(args: Array<String>) {
for (arg in args) {
print(arg)
}
args.map({ print(it) })
args.map() {
print(it)
}
args.map { print(it) }
args.map(::print)
}
Tips:命令行参数可以在run--Edit Configuration中修改。
第一种没有什么好说的,与Java类似。
-
第二种开始,使用了map方法进行遍历,map方法是Kotlin中的Arrays类提供的遍历集合的函数,定义如下:
//map方法是一个扩展方法,只有一个Lambda表达式参数,接收的是一个T类型的参数,返回R类型。T类型就是我们要迭代的String类型,而R类型没有明确指定 public inline fun <T, R> Array<out T>.map(transform: (T) -> R): List<R> { return mapTo(ArrayList<R>(size), transform) }
因此呢,map方法可以传进去一个 {表达式} 作为参数。而print接收一个it参数,it是迭代的String,返回的是空类型(Kotlin中空类型是Unit),完全符合map中的Lambda表达式的要求。因此可以传进去。
- 第三种,既然map只有一个Lambda表达式参数,那么可以把大括号后置,这是第三种。
- 第四中,在第三种的基础上,既然大括号可以省略,那么前面的()也省略了。
- 第五种,直接把函数的引用传进去(通过两个分号::)
Kotlin中的空用Unit类型表示,无返回值的函数可以这样写(一般无返回值直接省略而已):
fun main(args: Array<String>) :Unit{
}
使用flatMap扁平化集合
现在有一个需求:
将apple_bus_cat dog_egg_fly good_hook_it
扁平化输出为:apple:5 bus:3 cat:3 dog:3 egg:3 fly:3 good:4 hook:4 it:2
在Java中是这样做的:
public static void main(String[] args) {
for (String arg : args) {
String[] split = arg.split("_");
for (String s : split) {
System.out.print(s + ":" + s.length() + " ");
}
}
}
在Kotlin中是通过flatMap进行扁平化,然后再通过map进行遍历输出的:
fun main(vararg args: String) {
args.flatMap {
it.split("_")
}.map {
print("$it:${it.length} ")
}
}
程序中我们用到了flatMap对集合进行了一次扁平化,返回了一个数组,最终丢给map。下面我们来看看flatMap的源码:
//实际上flatMap也是传入一个Lambda表达式,将T类型(String)的数据转换成可迭代的R类型数据,因此String的split符合要求
public inline fun <T, R> Array<out T>.flatMap(transform: (T) -> Iterable<R>): List<R> {
return flatMapTo(ArrayList<R>(), transform)
}
//flatMap最终会调用flatMapTo方法,对集合进行扁平化,把每一个转换出来的数据都放到一个列表里面,因此,虽然我们有apple_bus_cat dog_egg_fly good_hook_it三组数据,但是最终被切割成apple bus cat dog egg fly good hook it这样的一个数组,然后再通过map进行遍历输出即可
public inline fun <T, R, C : MutableCollection<in R>> Array<out T>.flatMapTo(destination: C, transform: (T) -> Iterable<R>): C {
for (element in this) {
val list = transform(element)
destination.addAll(list)
}
return destination
}