IT狗

Kotlin: Java 6 废土中的一线期望

客岁,Java8 公布了,增加了许多新特征和晋升,比方lambda,stream。Java 9 的尺度也曾经在订定了。但是跨越对折的 Android 装备仍在运转着 Java 6,咱们要怎样能力用上新的现代化言语呢?

在 DroidCon NYC 2015 的这个共享里,Michael Pardo 引见了 Kotlin: 由 JetBrains 开发出的 JVM 静态言语。Kotlin 由许多新的特征,比方 lambdas,类扩大(class extensions),和 null 平安(null-safty)。它清新明了,同时由很高的互操纵性(interoperable)。

Transcription below provided by Realm: a replacement for SQLite that you can use in Java or Kotlin. Check out the docs!
Sign up to be notified of new videos — we won’t email you for any other reason, ever.
Save the date for Droidcon SF in March — a conference with best-in-class presentations from leaders in all parts of the Android ecosystem.

Kotlin (0:00)

大伙儿好,我是 Michael Pardo,明天我要给大伙儿展现一下 Kotlin 这门言语,同时看看他怎样让你在 Android 开发的时间更快乐,更有效率。

Kotlin 是一个基于 JVM 完成的静态言语。Kotlin 是 JetBrains 缔造并在连续保护这门言语,对,就是阿谁缔造了 Android Studio 和 IntelliJ 的公司。

Kotlin 有几个中心的目的:

清新:帮你淘汰完成同一个功用的代码量。
易懂:让你的代码更清新浏览,同时易于分明。
平安:移除你可能会犯毛病的功用。
通用:基于 JVM 和 Javascript,你可以在许多处所运转。
互操纵性:这就意味着 Kotlin 和 Java 可以彼此挪用,同时 Jetbrains 的目的是让他们 100% 兼容。
为何不等 Java 8?

Java 和 Android: 一段汗青 (1:11)

咱们来看看 Java 和 Android 的汗青和他们的干系。 在 2006 年,Java 6 公布了。几年今后,Android 1 的 Alpha 版本公布了,四年后,Java 7 公布了。Android 在 2 年后紧随厥后的开端支撑 Java 7。客岁,Java 8 又公布了。

你想一想,你甚么时间能力用上 Java 8? 可能你学的很快,然后就能用上 Java 8。但是 Android 怎样说都得几年后能力开端支撑 Java 8,大伙儿顺应 Java 8 又须要很长时间。Android 此刻的碎片化很严峻,Java 7 只支撑 API 19 及上述。假如用了 Java 7,那你的 App 用户群一会儿就少了一半。即使咱们此刻有了 Java 8,100% 的掩盖到了统统的用户装备上,但是 Java 自身仍是好多成绩的。

咱们来看看 Java 的一些成绩。

Java 有哪些成绩? (2:22)

空引用(Null references):连空引用的发明者都成这是个 billion-dollar 毛病(拜见)。非论你费多大的工夫,你都没法防止它。因为 Java 的范例零碎就是不平安的。
原始范例(Raw types):咱们在开发的时间老是会为了坚持兼容性而卡在范型原始范例的成绩上,咱们都分明要积极防止 raw type 的正告,但是它们究竟是在言语层面上的存在,这肯定会引发曲解和不平安要素。
协变数组(Covariant arrays):你可以创立一个 string 范例的数组和一个 object 型的数组,然后把 string 数组分派给 object 数组。这样的代码可以经过编译,但是一旦你测验考试在运转时分派一个数给阿谁数组的时间,他就会在运转时抛出极度。
Java 8 存在高阶要领( higher-order functions ),但是他们是经过 SAM 范例 完成的。SAM 是一个单个笼统要领,每一个函数范例都须要一个对应的接口。假如你想要创立一个并不存在的 lambda 的时间可能不存着对应的函数范例的时间,你要本身去创立函数范例作为接口。
泛型中的通配符:诡异的泛型老是难以操纵,难以浏览,誊写,和分明。对编译器而言,极度查抄也变得很坚苦。
咱们来索求下 Kotlin 是怎样办理下面的说到的这些成绩的。

Kotlin To The Rescue! (4:24)

适才咱们说到过的这些不足,Kotlin 一般间接移除那些特征。同时它也加了一些新的特征:

Lambda 表达式
数据类 (Data classes)
函数字面量和内联函数(Function literals & inline functions)
函数扩大 (Extension functions)
空平安(Null safety)
智能变换(Smart casts)
字符串模板(String templates)
主机关函数(Primary constructors)
类拜托(Class delegation)
范例揣度(Type inference)
单例(Singletons)
声明点变量(Declaration-site variance)
区间表达式(Range expressions)
咱们将在这篇美文里说起上述大大部分特征。Kotlin 之以是能跟随者 JVM 的生态零碎不时地前进,是因为他没有任何限定。它编译出来的恰是 JVM 字节码。在 JVM 看来,它就跟其他言语一样样的。事实上,假如你在 IntelliJ 可能 Android Studio 上用 Kotlin 的插件,它自带里一个字节码检察器,可以显现每一个要领天生的 JVM 字节码。

Hello, Kotlin (5:19)

咱们来看看根本语法。下面是一个最清新的,用 Kotlin 誊写的 Hello World:

fun main(args: Array<String>): Unit {  println("Hello, World!")}

只需一个函数和一个 print 语句。不须要包声明和类引用声明。这就是一个 Kotlin Hello World 程式的统统代码。声明函数的关键字是 fun,fun 后面跟的是函数的称号,然后括号包裹起来的是函数参数,这个跟 Java 近似。

然而,在 Kotlin 里,得把参数名放在后面,参数范例放在后面,用一个冒号离隔。函数的前往范例在末了,这个跟 Java 放在后面方法不太一样。假如一个函数没有前往任何范例,可以前往一个 Unit 范例,固然也可以省略。挪用 Kotlin 尺度库中的函数 println 就能打印 Hello World 出来,实践上它终极挪用了 Java 的 system.out.println。

fun main(args: Array<String>) {  println("Hello, World!")}

接下来,咱们来从 “Hello World” 中采集 “World” 这个词,并把这个词放到一个变量中。var 关键字后跟的是变量的称号。Kotlin 支撑字符串内拔出变量,只用在字符串内用 $ 标识表记标帜开首,随后跟上输入变量的变量名就可以,就像以下这样:

fun main(args: Array<String>) {  var name = "World"  println("Hello, $name!")}

随后,咱们来查抄咱们是否是给 main 函数传送了参数。先来断定这个字符串数组是否是空,假如不为空,咱们把第一个字符串分派给 name 变量。Kotlin 里有个 val 范例的声明要领,近似 Java 里的 final,也就是常量。

fun main(args: Array<String>) {  val name = "World"  if (args.isNotEmpty()) {    name = args[0]  }  println("Hello, $name!")}

在咱们编译这个程式的时间,咱们碰到一个成绩:没法从头分派新的值给一个常量。一种办理要领是用内联的 if-else 要领。Kotlin 里的大部分的代码块都支撑前往值。假如语句进入了 if 代码块儿,也就是说 args 非空,那末就前往 arg[0],不然前往 “World”。 if-else 语句竣事后,就间接赋值给咱们之前声明的 name 常量,下面的案例就是前提赋值代码块:

fun main(args: Array<String>) {.  val name = if (args.isNotEmpty()) {    args[0]  } else {    "World"  }  println("Hello, $name!")}

咱们可以把下面的代码用一行来誊写,看起来有点像 Java 里的三目运算符。移撤除那些大括号后,看起相称美丽:

val name = if (args.isNotEmpty()) args[0] else "World"

类语法 (5:19)

咱们来看看类。类的界说要经过 class 关键字,跟 Java 里的一样,关键字后是类名。Kotlin 有一个主机关函数,咱们可以间接将机关函数参数列表写在类的声明处,还可以间接用 var 可能 val关键字将参数声明为成员变量(又称:类属性),以下:

class Person(var name: String)

继续之前的案例,有了主机关函数今后,咱们就不再须要成员变量赋值语句了。在 Kotlin 里创立实例的时间,不用运用 new 关键字。你只须要指明创立的范例名就可以创立实例了。

class Person(var name: String)fun main(args: Array<String>) {  val person = Person("Michael")  println("Hello, $name!")}

很清新发明,字符串插值实践上是毛病的,因为 name 指向的是一个不存在的变量了。咱们可以用适才说到的 字符串插值表达式 ,即用 $ 标识表记标帜和大括号包裹想要拔出的变量,来修复这个成绩:

class Person(var name: String)fun main(args: Array<String>) {  val person = Person("Michael")  println("Hello, ${person.name}!")}

下面是 enum 类。罗列跟 Java 里的罗列很像。界说一个罗列的要领以下:

enum class Language(val greeting: String) {  EN("Hello"), ES("Hola"), FR("Bonjour")}

咱们来给 Person 类增加一个叫 lang 的属性,表示一团体的所说的言语。

class Person(var name: String, var lang: Language = Language.EN)

Kotlin 支撑参数默许值,如上:language 的默许值就是 Language.EN,这样就可以在创立实例的时间疏忽这个参数,除非你要转变 language 的属性值。咱们来把这个案例变得更面向对象一些,给 person 增加一个打招呼的要领,清新地输入特定言语打招呼的要领另有人名:

enum class Language(val greeting: String) {  EN("Hello"), ES("Hola"), FR("Bonjour")}class Person(var name: String, var lang: Language = Language.EN) {  fun greet() = println("${lang.greeting}, $name!")}fun main(args: Array<String>) {  val person = Person("Michael")  person.greet()}

此刻在 main 函数里挪用 person.greet() 要领,看看是否是很酷?!

纠合和迭代 (11:32)

val people = listOf(  Person("Michael"),  Person("Miguel", Language.SP),  Person("Michelle", Language.FR))

咱们可以用尺度库函数 listOf 要领创立一个 person 列表。遍历这些 person 可以用 for-in 关键字:

for (person in people) {  person.greet()}

随后,咱们可以在每次遍历的时间履行 person.greet() 要领,以至可以更清新,间接挪用 people 纠合的扩大要领 forEach,传入一个 lambda 表达式,在表达式里用 it 表示每次遍历到的person 对象,然后挪用它们的 greet 要领。

people.forEach { it.greet() }

咱们来创立两个新的类,每一个都传入一个默许的语系。咱们可以不再像适才那样一次又一次声明,可以间接用继续的要领来完成。下面是一个扩大版本的 Hello World。展现了许多 Kotlin 的特征:

enum class Language(val greeting: String) {  EN("Hello"), ES("Hola"), FR("Bonjour")}open class Person(var name: String, var lang: Language = Language.EN) {  fun greet() = println("${lang.greeting}, $name!")}class Hispanophone(name: String) : Person(name, Language.ES)class Francophone(name: String) : Person(name, Language.FR)fun main(args: Array<String>) {  listOf(    Person("Michael"),    Hispanophone("Miguel"),    Francophone("Michelle")  ).forEach { it.greet() }}

Kotlin 在 Java 上加了甚么特征? (13:11)

下面,咱们来看看 Kotlin 在 Java 之上加了哪些更好用的新特征。

范例推导 (13:18)

你可能在其他言语中发现过范例推导。在 Java 里,咱们须要本身声明范例,变量名,和数值。在 Kotlin 里,按次好多不一样,你先声明变量名,然后是范例,然后是分派值。许多状况下,你不须要声明范例。一个字符串字面量足以指明这是个字符串范例。字符,整形,长整形,单浮点数,双浮点数,布尔值都是可以不需要显性声明范例的。

var string: String = ""var string = ""var char = ' 'var int = 1var long = 0Lvar float = 0Fvar double = 0.0var boolean = truevar foo = MyFooType()

只需 Kotlin 可以推导,这个划定规矩异样实用与其他一些范例。一般,假如是个别地方变量,当你在声明一个值可能变量的时间你不须要指明范例。在一些没法推导的场景里,你才须要用完备的声明变量语法指明变量范例。

空平安(null-safety) (14:22)

Kotlin 一个壮大的特征是空平安。咱们来看几个案例:

String a  = null;System.out.println(a.length());

在 Java 里,声明一个 string 范例,赋一个 null 给这个变量。一旦咱们要打印这个字符串的时间,会在运转时曝出空指针毛病,因为咱们在测验考试去读一个空值。下面是这个成绩的 kotlin 写法,咱们界说一个空值,但是在咱们测验考试操纵它之前,Kotlin 的编译器就报告了咱们成绩地点:

val a:String = null

曝出的毛病是:咱们在测验考试着给一个非空范例分派一个 null。在 Kotlin 的范例系统里,有时间范例和非空范例。范例零碎识别出了 string 是一个非空范例,并且制止编译器让它以空的状况存在。想要让一个变量为空,咱们须要在声明后面加一个 ? 号,同时赋值为 null。

val a: String? = nullprintln(a.length())

此刻,咱们修复了这个成绩,继续向下:就像在 Java 里一样,咱们测验考试打印 stirng 的长度,但是咱们碰到了跟 Java 一样的成绩,这个字符串有可能为空,不外幸亏的是:Kotlin 编译器匡助咱们发明了这个成绩,而不像 Java 那样,在运转时爆出这个毛病。

编译器在长度输入的代码前中止了。想要让编译器编译下去,咱们得在挪用 length 要领的时间斟酌到可能为空的状况,要末赋值给这个 string,要末用一个问号在变量名后,这样,代码履行时在读取变量的时间查抄它是否是为空。

val a: String? = nullprintln(a?.length())

假如值是空,则会前往空。假如不是空值,就前往实在的值。print 碰到 null 会输入空。

Ternary Null (16:19)

int length = a != null ? a.length() : -1

下面的代码你可能在 Java 里见到过。用三目运算符取值,查抄是否是为空,假如为空则前往实在的长度,不然前往 -1,Kotlin 里又相反的完成:

var length = if(a!= null) a.length() else -1

假如 a 不是 null, 那末就可以间接读值,不然前往默许值。这里用 elvis操纵符 完成的简写:

var length = a?.length() ?: -1

咱们用 ?号做了一个内联空查抄。假如你还记得适才我说的,假如 a 是 null,第一个 ?表达式就会前往 null ,假如 elivs 操纵符 左边是空,那末他就会前往右边,不然间接前往左边的值。

智能变换 (17:30)

Kotlin 支撑范例智能变换的特征。 假如一个个别地方对象传入一个范例查抄,你可以间接经过这个范例来操纵,而不须要再本身做变换,看下面的案例你就分明了:

if (x is String) {  print(x.length())}

咱们查抄了 x 是否是一个字符串,假如是,就打印它的长度。做范例查抄,咱们须要用到 is 关键字,事实上跟 Java 里的 instanceOf 一样。道理很清新,咱们如果经过了范例查抄,咱们就能把它当作这个范例来运用。

if (x !is String) {  return}print(x.size())

逆操纵也没有成绩。下面这个案例查抄了是否是是一个非字符串变量。假如不是,则间接前往。在咱们断定了今后就可以以为它是一个字符串了,咱们也不需要在做显式的范例变换。

if (x !is String || x.size() == 0) {  return}

下面的案例里,咱们查抄了一个字符串是否是是一个非字符串变量,假如左边的值是 false,就会挪用右边的 or 断定。
when 语句也实用下面的划定规矩,when 事实上是一个加强版的 switch。

when(x) {  is Int -> print(x + 1)  is String -> print(x.size() + 1)  is Array<Int> -> print(x.sum())}

咱们只需做了范例查抄,范例统统都会主动变换。从范例查抄到范例主动变换,就是 Kotlin 的智能变换。

字符串模板 (19:07)

许多言语都有字符串模板和字符串插值。下面的案例可能就是你在 Java 里常常用到的:

val apples = 4println("I have " + apples + " apples.")

你可以把 apples 变量和其他字符串串连起来。用更切合 Kotlin 作风的方法,你可以用插值的要领,在字符串顶用 $ 标识表记标帜前缀加变量名来表示这个字符串内容。

val apples = 4println("I have $apples apples.")

你也可以用下面的表达式:

val apples = 4val bananas = 3println("I have $apples apples and " + (apples + bananas) + " fruits.") // Java-esqueprintln("I have $apples apples and ${apples+bananas} fruits.") // Kotlin

在 Java 中,可能最容易看到的计划是在须要显现个数的处所,用加号操纵苹果和香蕉的个数,然后将字符串都串起来。但在 Kotlin 中,可以用前缀 $ 标识表记标帜加上大括号将操纵语句包裹起来表示操纵语句的结果。

区间表达式 (20:00)

你可能在其他的言语里见到过这样的表达式。简直, Kotlin 的很多特征是参考自其他言语里。下面这个表达式:假如 i 大于即是 1,并且小于即是 10,就将其打印出来。咱们检测的局限是 1 到 10。

if (1 <= i && i <= 10) {  println(i)}

事实上咱们可以用 intRange 函数来完成这个操纵。咱们传入 1 和 10,然后挪用 contains 函数来断定是否是在这个局限里。咱们打印出 i 就可以。

if (IntRange(1, 10).contains(i)) {  println(i)}

这个还可以用扩大函数来完成,1.rangeTo 创立了一个 1 到 10 的 intRange,咱们可以用 contain 来断定它。

更完善的而清新的写法,是用下面的操纵符:

if(i in 1..10) { ... }

.. 是 rangeTo 的一个别号,它实践面前事情道理仍是 rangeTo。

咱们还可遍历一个区间,比方:可以用 step 关键字来注定每次遍历时间的腾跃幅度:

for(i in 1..4 step 2) { ... }

也可以逆向迭代,可能逆向遍历并且掌握每次的 step:

for (i in 4 downTo 1 step 2) { ... }

在 Kotlin 里,也可以分离不一样的函数来完成你想要的区间遍历。可以遍历许多不一样的数据范例,比方创立 strings 可能你本身的范例。只需切合逻辑就行。

高阶函数 (22:55)

许多言语曾经支撑了高阶函数,比方 Java 8,但是你并不可以用上 Java 8。假如你在用 Java 6 可能 Java 7,下面的案例完成了一个具有过滤功用的函数:

public interface Function<T, R> {  R call(T t);}public static <T> List<T> filter(Collection<T> items, Function<T, Boolean> f) {  final List<T> filtered = new ArrayList<T>();  for (T item : items) if (f.call(item)) filtered.add(item);  return filtered;}filter(numbers, new Function<Integer, Boolean>() {  @Override  public Boolean call(Integer value) {    return value % 2 == 0;  }});

咱们起首要声明一个函数接口,接收参数范例为 T,前往范例为 R。咱们用接口中的要领遍历操纵了目的纠合,创立了一个新的列表,把切合前提的过滤了出来。

fun <T> filter(items: Collection<T>, f: (T) -> Boolean): List<T> {  val filtered = arrayListOf<T>()  for (item in items) if (f(item)) filtered.add(item)  return filtered}

下面的代码是在 Kotlin 下的完成,是否是清新许多?咱们挪用的时间以下:

kotlin filter(numbers, { value -> value % 2 == 0 })

你可能也发明了,咱们没有界说任何的函数接口,这是因为在 Kotlin 中,函数也是一种数据范例。发现 f:(T) -> Boolean 这个语句了吗?这就是函数范例作为参数的写法,f 是函数别号,T是函数接收参数,Boolean 是这个函数的前往值。界说完成后,咱们随后就能跟挪用其他函数一样挪用 f。挪用 filter 的时间,咱们是用 lambda 表达式来传入过滤函数的,即:{value ->value % 2 = 0}。

因为函数范例参数是可以经过函数声明的署名来推导的,以是事实上另有下面的一种写法,大括号内就是第二个参数的函数体:

filter(numbers) {  it % 2 == 0}

内联函数 (25:27)

内联函数和高阶函数常常一路见到。在某些场景下,当你用到泛型的时间,你可以给函数加上inline 关键字。在编译时,它会用 lambda 表达式更换掉全部函数,全部函数的代码会成为内联代码。

假如代码是这样的:

inline fun <T> filter(items: Collection<T>, f: (T) -> Boolean): List<T> {  val filtered = arrayListOf<T>()  for (item in items) if (f(item)) filtered.add(item)  return filtered}filter(numbers) { it % 2 == 0 }

由 inline 关键字在编译后会酿成以下这样:

val filtered = arrayListOf<T>()for (item in items) if (it % 2 == 0) filtered.add(item)

这也意味着咱们能完成一些惯例函数完成不了的。比方:下面这个函数接收一个 lambda 表达式,但并不可以间接前往:

fun call(f: () -> Unit) {  f()}call {  return // Not allowed}

但是假如咱们的函数酿成内联函数,此刻咱们就能间接前往了,因为它是内联函数,会主动和其他代码混淆在一路:

inline fun call(f: () -> Unit) {  f()}call {  return // Now allowed}

内联函数也许可用 reified 范例。下面这个案例就是一个实在场景下的函数,经过一个 View 寻觅范例为 T 的父元素:

inline fun <T : Any> View.findViewParent(): T? {  var parent = getParent()  while (parent != null && parent !is T) {    parent = parent.getParent()  }  return parent as T // Cast warning}

这个函数还好多成绩。因为泛型范例被擦除,以是咱们没法检测范例,即使咱们手工来做查抄,仍然会呈现 warning。

办理计划是:咱们给函数参数范例加上 reified 关键字。因为函数会被编译成内联代码,以是咱们此刻就能手工查抄范例清除正告了:

inline fun <reified T : Any> View.findViewParent(): T? {  var parent = getParent()  while (parent != null && parent !is T) {    parent = parent.getParent()  }  return parent as T // Type cast allowed}

函数扩大 (27:20)

函数扩大是 Kotlin 最壮大的特征之一。下面是一个东西函数,检测 App 是否是运转在 Lollipop 可能更高的 Api 之上,它接收一个整数参数:

public fun isLollipopOrGreater(code: Int): Boolean {  return code >= Build.VERSION_CODES.LOLLIPOP}

经过 被扩大范例.函数 的写法,就能将函数酿成被扩大范例的一部分,写法以下:

public fun Int.isLollipopOrGreater(): Boolean {  return this >= Build.VERSION_CODES.LOLLIPOP}

咱们不在须要参数,想要在函数身体里挪用整数对象须要用 this 关键字。下面就是咱们的挪用要领,咱们可以间接在整数范例上挪用这个要领:

16.isLollipopOrGreater()

函数扩大可以是任何整形,字面量可能包装范例,也可以在标识表记标帜为 final 的类上做近似操纵。因为扩大函数不是真的给类增加代码,任何人都没有方法去修正一个类,它实践上是创立了一个静态要领,用语法糖来让扩大函数看着像是类自带的要领一样。

Kotlin 在 Java 纠合中充实操纵了扩大函数,这有一个案例操纵纠合:

final Function<Customer, Order> customerMapper = // ...final Function<Order, Boolean> orderFilter = // ...final Function<Order, Float> orderSorter = // ...final List<Order> vipOrders = sortBy(filter(map(customers,    customerMapper),    orderFilter),    orderSorter);

咱们对一个 customer 纠合,履行了 map, filter, 和 sort 操纵。嵌套的写法杂乱并且难以浏览。下面是尺度库的扩大函数写法,是否是清新了许多:

val vipOrders = customers    .map { it.lastOrder }    .filter { it.total >= 500F }    .sortBy { it.total }

属性 (30:55)

Kotlin 把属性也酿成了言语特征。

class Customer {  private String firstName;  private String lastName;  private String email;  public String getFirstName() { return firstName; }  public String getLastName() { return lastName; }  public String getEmail() { return email; }  public void setFirstName(String firstName) { this.firstName = firstName }  public void setLastName(String lastName) { this.lastName = lastName }  public void setEmail(String email) { this.email = email }}

下面是一个典范的 Java bean 类。可发现许多成员变量,和许多 getter, setter 要领,这但是只需三个属性的时间,就天生了这么多代码。来看看 Kotlin 的写法:

class Customer { var firstName: String = // ... var lastName: String = // ... var email: String = // ...}

你只须要将成员变量界说成一个变量就可以,默许是 public 的。编译器会主动天生 getter 和setter 要领。

主机关函数 (31:49)

Kotlin 中,类可以具有多个机关函数,这一点跟 Java 近似。但你也可以有一个主机关函数。下面的案例是咱们从下面的案例里衍生出来的,在函数头里增加了一个主机关函数:

在主机关函数里,可以间接用这些参数变量赋值给类的属性,可能用机关代码块来完成初始化。

class Customer(firstName: String, lastName: String, email: String) {  var firstName: String  var lastName: String  var email: String  init {    this.firstName = firstName    this.lastName = lastName    this.email = email  }}

固然,更好的要领是:间接在主机关函数里界说这些属性,界说的要领是在参数名前加上 var可能 val 关键字,val 是表示属性是常量。

class Customer(  var firstName: String,  var lastName: String,  var email: String)

单例 (35:53)

你可能常常会用到单例设计模式。比方一个 Logger 类,在 Java 里,有多种完成单例的写法。

在 Kotlin 里,你只需在 package 级别创立一个 object 就可以!非论你在甚么域里,你都可以像单例一样挪用这个 object。

object Singleton
比方下面是一个 looger 的写法:

object Logger {  val tag = "TAG"  fun d(message: String) {    Log.d(tag, message)  }}

你可以间接经过 Logger.D 的要领来挪用 D 函数,它在任何处所都是可用的,并且一直只需一个实例。

Companion Objects (37:00)

Kotlin 移除 static 的观点。一般用 companion object 来完成近似功用。你可能经常会发现一个 Activity 有一个 静态范例的 string,名叫 tag,和一个启动 Activity 的静态要领。Java 中的完成以下:

class LaunchActivity extends AppCompatActivity {  public static final String TAG = LaunchActivity.class.getName();  public static void start(Context context) {    context.startActivity(new Intent(context, LaunchActivity.class));  }}

在 Kotlin 下的完成以下:

class LaunchActivity {  companion object {    val TAG: String = LaunchActivity::class.simpleName    fun start(context: Context) {      context.startActivity(Intent(context, LaunchActivity::class))    }  }}Timber.v("Starting activity ${LaunchActivity.TAG}")LaunchActivity.start(context)

有了 companion object 后,就跟类多了一个单例的对象和要领一样。

类拜托 (37:58)

拜托是一个大伙儿都分明的设计模式,Kotlin 把拜托视为很紧张的言语特征。下面是一个在 Java 中典范的拜托写法:

public class MyList<E> implements List<E> {  private List<E> delegate;  public MyList(List<E> delegate) {    this.delegate = delegate;  } // ...  public E get(int location) {    return delegate.get(location)  }  // ...}

咱们有一个本身的 lists 完成,经过机关函数将一个 list 存储起来,存在里边的成员变量里,然后在挪用相干要领的时间再拜托给这个里边变量。下面是在 Kotlin 里的完成:

class MyList<E>(list: List<E>) : List<E> by list

用 by 关键字,咱们完成了一个存储 E 范例的 list,在挪用 List 相干的要领时,会主动拜托到 list 上。
译者注:参考 Kotlin 官方文档相识更多。

声明点变型(Declaration-Site Variance) (39:03)

这个可能是一个对比清新让人迷惘的主题。起首,咱们用一个协变数组来开端咱们的案例,下面的代码可以很好的编译:

String[] strings = { "hello", "world" };Object[] objects = strings;

string 数组可以正常的赋值给一个 object 数组。但是下面的不可:

List<String> strings = Arrays.asList("hello", "world");List<Object> objects = strings;

你不可以分派一个 string 范例的 list 给一个 object 范例的 list。因为 list 之间是没有继续干系的。假如你编译这个代码,会获得一个范例不兼容的毛病。想要修复这个毛病,咱们得用到 Java 中的点变型(use-site variance)去声明,所谓的点变型就是在声明 list 可接收范例的时间,用extends 关键字给出参数范例的可接收范例局限,比方近似以下的案例:

译者注:点变型只是一个名字,不要太在乎为何叫这个,清新分明就是近似通配符道理,详细可以检察这个维基页面。

public interface List<E> extends Collection<E> {  public boolean addAll(Collection<? extends E> collection);  public E get(int location);}

addAll 要领可以接收一个参数,参数范例为统统继续自 E 的范例,这不是一个详细范例,而是一个范例局限。每次挪用 get 要领时,仍然前往范例 E。在 Kotlin 中,你可以用 out 关键字来完成近似的功用:

public interface List<out E> : Collection<E> {  public fun get(index: Int): E}public interface MutableList<E> : List<E>, MutableCollection<E> {  override fun addAll(c: Collection<E>): Boolean}

下面的一系列被称为声明点变型,即在声明可接收参数的时间,就声明为它是可变的。比方下面案例:咱们声明参数是可以许可统统继续自 E 范例的,前往范例也为 E 的。

此刻,咱们有了可变和不可变范例的列表。可变性(variance) 事实上很清新,就是取决于咱们在声明的时间是行动。

译者注:事实上非论声明点变型(Declaration-Site Variance) 仍是 点变型(Use-site variance) 都是为了完成泛型的范例声明,标注泛型范例可支撑的局限,厘清泛型范例高低继续边境。参考Generic Types。

操纵符重载 (41:26)

enum class Coin(val cents: Int) {  PENNY(1),  NICKEL(5),  DIME(10),  QUARTER(25),}class Purse(var amount: Float) {  fun plusAssign(coin: Coin): Unit {    amount += (coin.cents / 100f)  }}var purse = Purse(1.50f)purse += Coin.QUARTER // 1.75purse += Coin.DIME // 1.85purse += Coin.PENNY // 1.86

下面的代码中,咱们创立了一个硬币罗列,每一个硬币罗列都表示一个特定数额的硬币。咱们有一个 Purse(钱包) 类, 它具有一个 amount 成员变量,表示钱包里此刻有几多钱。咱们创立了一个叫做 plusAssign 的函数,plusAssign 是一个保存关键字。这个函数会重载 += 操纵符,也就是说当你在挪用 += 标识表记标帜的时间,就会挪用这个函数。

随后,创立一个 purse 实例,可以间接用 += 操纵来完成给钱包里放钱出来。

让你的项目支撑 Kotlin (42:25)

让你的项目支撑 Kotlin 事实上极度清新,你须要让你的项目里启用 gradle 插件,Kotlin Android 插件,拷贝代码文件,引入尺度库就可以。

开端 Kotlin 之路

Kotlin 文档
迅速入门
Kotlin 和 Java 的不一样

Q&A (43:40)

Q: Kotlin 有无甚么缺陷?

Michael: Kotlin 的多数特征可能有功能成绩,比方 annotation 处置速率很慢。除此之外猛烈引荐。

Q: 支撑 Unit Test 么?

Michael: 支撑,咱们用Robolectric 和 Mockito 来做测试。

Q: Debug 的时间会很顺遂吗?

Michael: Kotlin 天生的只是 JVM 字节码罢了,并且 Kotlin 和 IntelliJ 共存的极度好,究竟都是一家做的。咱们之前用的 retrolambda,但是他天生的字节码老是好多小成绩。

Q: Kotlin 对 Jack and Jill 的支撑怎样?

Michael: Jack and Jill 和 Kotlin 不可以一路事情。

Q: Kotlin 上用反射怎样样?

Michael: Kotlin 上用反射的一大成绩是回引入一个很大的库…… 不外,这些你可以用 Java 来做。

此文由 IT狗 编辑,本网站所发布展示的作品/文章版权归原作者所有,任何商业用途均须联系作者!

相关推荐

评论 暂无评论