Advertisement

“Kotlin in Action”读书笔记

阅读量:

Kotlin in Action

Chapter 2

1.small cast

一旦确定了目标类型,编译器能够自动处理这种转换,并不需要像Java那样显式地进行类型转换

例子:

interface Expr

class Num(val value: Int) : Expr

class Sum(val left: Expr, val right: Expr) : Expr

fun eval(e: Expr): Int =

when(e){

将数值类型赋值给e对象的属性

is Sum -> eval(e.left) + eval(e.right)

else -> throw IllegalArgumentException("Unknown expression")

}

2.range

i in 0..100表示0到100区间内的数字

..

正向可用until 代替”..”反向使用downTo可用step进行跳跃式的递增(减)

3.异常

Kotlin放弃了将异常分为检查性处理与未检查性处理的方法,并因此提供了更大的灵活性——我们可以选择性地选择是否捕获特定类型的异常。在实际应用中,在某些情况下对运行时异常的捕捉更为有意义——例如,在实际应用中,我们通常会关注那些可能影响应用程序正常运行的行为。

fun readNumber(reader: BufferedReader):Int?{

try {

val lin = reader.readLine()

return Integer.parseInt(lin)

}catch (e: NumberFormatException){ //此处的运行时异常(Kotlin与Java一致)无需捕获即可处理,并且具备更高的实用价值.

return null

}finally {

reader.close() //Java中需要对这句进行异常捕捉,而kotlin不用

}

}

val reader = BufferedReader(StringReader("239"))

println(readNumber(reader))

Chapter 3

1.集合

setOf -> LinkedHashSet

listOf -> ArrayList

map -> HashMap

这些关键字被用来迅速构建集合;同时此等关键字被用来构建其所属对象的Java类类型

2.顶级方法和属性

请不要被各类util类所限制,在同一个文件内定义方法和属性时可以在整个项目范围内获取。

@file:JvmName("StringFunctions")

顶级文件会自动生成与其相同名称的Java类,并将该文件的方法转换为该Java类中的静态方法。其中@file:JvmName用于设置生成的Java类名称。

3.扩展函数(大招来了)

一个方法可以作为某个类的成员而不在该类中对其进行定义。

在本质上与静态方法相同,并且同样属于静态绑定关系。因此,在这种情况下,默认会按照声明时所定义的数据类型来决定将要执行的功能。

4.数组解包unpack

‘*’运算符至于Array(数组)前可以将其解包,至于listOf等之中。

5.中缀函数infix

在函数声明中使用infix关键字时,在执行方法时不需再使用点号和圆括号而是可以直接使用空格代替。

6.支持函数内部调用函数

Chapter 4

1.类的基本特性

使用”:“替代java中的implements和extends

kotlin中override关键字是强制要使用的

kotlin允许对接口中的方法进行默认实现

kotlin中的类和方法遵循final作为默认设置的规定,在进行继承或重写功能时,则必须明确声明为open

该方法在继承时将默认自动转换为Open。
若希望该对象无法通过override机制进行更改,则需将其声明为Final。
(若无Final关键字,则该对象将保持Open状态)

被abstract修饰的类或者方法时默认open的

2.可见性问题

kotlin提供了与public、private、protected相同的可访问修饰符,并且在这些修饰符的使用上与Java有所不同。然而,在kotlin中,默认情况下会采用public修饰符

kotlin中不用package作为可见性控制的要素,package只提供命名空间

kotlin中新内联访问属性internal用作模块访问权限(其中_A-module_是Kotlin文件集合的集合体。它可能是一个IntelliJ IDEA模块、一个Eclipse项目、一个Maven或Gradle项目,或者通过Ant任务编译的一组Kotlin文件)。

在Kotlin中使用protected修饰符时,仅由子类可见;而与Java中的情况不同的是,在Kotlin中无需额外获得同一package内的访问权限。

3.内部类问题

在kotlin语言中未使用任何关键字定义的内部类相当于Java中的static inner class且没有外引(external references)。

kotlin如果想要持有外部类的引用,需要用inner修饰内部类

class Outer {
inner class Inner {

fun getOuterReference(): Outer = this@Outer }

} //获取外部类引用的语法

4.sealed关键字

当关键字else可以通过该种方式消除其影响:将所有子类声明为内联(inner)类型,并通过 sealed修饰父类型class A。这样一来,在class A之外(除自身内部和同一文件外),均无法进行子类型的声明;而对于旧版本的1.1及以下版本,则只能在本类别内部禁止外部继承。

A sealed class is capable of having subclasses, which must all be declared in the same file as itself. (Prior to Kotlin 1.1, these rules became even more stringent: classes had to be nested within rather than outside of declarations for a seal.) https://kotlinlang.org/docs/reference/sealed-classes.html

5.类构造问题

在基类与接口实现类之间的语法差异主要体现在:因为基类必须调用其构造函数以启动父类初始化过程,即在基类名称后添加括号以指定参数传递位置;而实现接口则无需进行此类操作。

以下是对多参数构造函数的实现方案;而仅针对单一参数的情形,则采用如下方式:class A (val args :Int)。

class MyButton : View { constructor(ctx: Context)

: super(ctx) { }//...

constructor(ctx: Context, attr: AttributeSet)

: super(ctx, attr) {

// ... }

}

6.接口中的属性

在接口中定义属性时需要特别注意的是:虽然可以直接声明属性的位置(如字段名),但在实际赋值初始化时则需要遵循特定规则。具体来说,在接口中声明的属性无法直接进行赋值操作;其初始化过程必须由该接口所对应的实现类代码层通过继承机制(即使用override关键字)来完成这一操作。此外,在接口层面上定义的属性可以通过get()方法进行功能声明,并且这种设计方式能够有效避免在实现类层面上出现强制性地对基类中的重写现象。

interface User{

val name: String

get() = "111"

val nickname : String

get() = "nick" + name

}

class NetUser():User{

}

7.类中field的访问权限

set()和get()操作通常具有与对应数据字段相同的基本可访问性;然而,可以让这些操作单独设置各自的访问权限

class LengthCounter {

var counter: Int = 0

private set

public get

}

8.”==“和“equals”

在Kotlin编程语言中,默认情况下使用==符号时会调用对象所属类的equals方法来进行值比较。若目标是进行引用对象(reference)的比较,则应使用===符号。这一特点与Java编程语言的主要区别较为显著。

插入set时的情况

initially comparing the hash codes of these entities. Only when their hash codes match will the actual values be examined.

9.data class 数据类

数据类定义会自动实现toString(),hashCode(),equals()方法,
这些方法的实现通常与类的主要构造器携带的字段相关联。
此外,默认情况下不带参数的copy()方法会产生源对象的副本,
当传递参数时,它会创建一个修改后的副本。

10.代理类“by”

像代理模式一样,在Java中这种做法需要一个对象同时实现多个接口,并由该对象处理这些接口对应的所有方法。毫无疑问这种方法会导致大量的重复代码Kotlin提供了一种优雅简洁的方式来解决这个问题这种方法允许我们 selectively override某些方法而无需完全重写所有method。

interface A{

fun a()

}

class B : A{

overriden fun a(){

println(“aaa”)

}

}

class DelegateA(a: A = B()) : A by a

11.object(the final big part of Kotlin’s class story)

当一个类被Object关键字修饰时, 其对象在其定义时即被生成. 这种情况下无需也不允许为该类显式地定义构造函数. 在Kotlin中, object是一个实现单例模式的有效手段. The compilation of an object in Kotlin results in a class containing a static field that maintains a single instance.

companion:

在类级别编写静态方法时,在某种程度上可充当替代方案用于解决特定问题;但在某些情况下会遇到以下情形:

仅在本类范围内获取函数功能, 不希望在包级别中的任何函数进行访问. 其实现过程可借助companion机制完成.

val listener = object :Listener{

override fun onEvent() {

}

} //object 关键字同样可作为匿名内部类的实现方式

Chapter5

program with Lambdas(令人迷之神往~~~)

1.kotlin collection特性

people.map(Person::name).filter { it.startsWith("A") } //EAGER

people.asSequence() .map(Person::name)
.filter { it.startsWith("A") } .toList() //LAZY

采用两种方法运行后所得输出相同;对比之下,在不启用asSequence时会生成临时HoldOnCollection;与之相比,则可规避临时值生成。因此,在处理大量数据的情况下建议优先选用asSequence方法。

chapter6

1.nullable

在方法中的变量参数如果没有声明为null类型,则会被传递入null值。当这些被传递的参数是null值时,在编译阶段就会出现错误。而像Java这样处理的话,则是在程序运行时才会出现异常。

允许设为 Null 的属性无法指定为其 不可 Null 类型;即使允许设为 Null 的属性已经被指定为了 非 Null 类型,则仍然无法指定为其 不可 Null 类型。

person.company?.address ?: throw IllegalArgumentException("No address")

“?.”组装的链中,一旦有中间任意一环出现null的情况,则会执行?:后面的方法。

2.as?关键字

3.!!关键字

In essence, you are instructing the compiler. You are indicating that you know the value is not null and are prepared to throw an exception in case.

4.let

let可以让一个可null的类型转换为非null类型

pastedGraphic.png

5.lateinit

对于标志类中的该字段,在对象生命周期内无需同时进行初始化操作,并可在后续方法调用时完成赋值过程。当采用依赖注入模式时,默认情况下此类字段会被适当管理以确保正确性与安全性。

6.泛型中的null问题

在本例中, 函数 foo 接收一个参数 t, 其实质是可以传递 null 值的。这是因为类型 <T> 实际上相当于 <T: Any>。如果不想支持 null 参数, 则应将类型修改为 <T: any>

7.Java和kotlin联合的null

Java类型的赋值方式不仅支持将Java类型赋值给Kotlin中的nullable类型还支持将Java类型赋值给Kotlin中的non-null类型

8.原始类型

kotlin中没有像java中区分原始类型和与之对应的包装器。

在大多数情况下,在kotlin中的Int会转译为对应的Java基本类型int值;然而,在以下两种特殊情况下可能会出现异常行为

(1)在Int用于Collection中的参数时,会被编译成Java中的Integer

在编译过程中形成一个包装器的场景中,在变量声明为nullable类型的情况下(由于原始数据类型不支持null引用),这种配置得以实现。

在Kotlin中不支持自动的upward widening或downward narrowing,例如将int赋值给long时(反之亦然)

要显示调用Int.toLong()方法完成转型

LiteralsoftypeLong usetheLsuffix:123L.
LiteralsoftypeDouble usethestandardrepresentationoffloating-point numbers:0.12,2.0,1.2e10,1.2e-10.
LiteralsoftypeFloat usethe'f'orFsuffix:123.4f,.456F,1e3f. Hexadecimalliteralsusethe0xor0Xprefix(suchas0xCAFEBABEor0xbcdL). Binaryliteralsusethe0bor0Bprefix(suchas0b000000101).

自动的向上转型:kotlin中 1 + 1L会得到一个Long型

9.Unit Nothing篇幅较少

10.只读和可变Collection

Collection和MutableCollection分别代表该类型的集合是否为不可变或可变

当明确指定函数参数为MutableCollection时,在传递Collection类型的引用(即便知道其指向的对象是可变的)也会不可行。然而,在相反情况下则是可行的。

在Java中,Collection接口划分为可变和不可变类型,在这些情况下当Kotlin调用这些函数时无需对传入类型的集合进行区分,Kotlin支持这两种集合类型:ImmutableSet与MutableSet

该文档中定义了Collection接口分为两类的子接口,并在Kotlin语言中的相关数据接口实现部分也做了相应的划分。具体来说,在Kotlin语言中与Collection相关的各种数据结构如集合(set)、列表(list)和映射(map)也将各自对应地提供两种不同的数据接口实现。

val strings = listOf("a", "b", "c")

println("%s/%s/%s".format(*strings.toTypedArray())) a/b/c

*符号用于将array转化为可变参数传入

Array表示的是Java中的Integer[];为了生成原始类型的数组,请使用IntArray

Array 使用toIntArray可将包装器数组转换为原始数组

Chapter7

1.operator(单目运算符,双目运算符,集合存取符号”[]”)

Java中的数学运算符只能用于原始类型、String类型的运算。

kotlin中提供了一种方法来实现自定义操作符+-*%的重载以使得非基本数据类型的运算得以执行

位运算符不可以重载

2.plus 与 assignPlus

若采用var关键字进行变量声明,在执行a += b操作时可能会导致冲突出现。具体而言,在这种情况下编译器无法确定调用哪个方法会导致错误信息产生。

应对该挑战时需将变量指定为val,并可同时采用直接使用函数名的方式实施这两种策略

在操作可变 collection(MutableCollection)时会就地修改其状态,在对不可变 Collection 进行操作时将生成一个基于原有数据的新副本。

3.结构声明

data class可以直接解构,而普通class需要重载操作符componentN()方法。

4.代理属性 delegate property

用于延迟加载:

假设存在这样的一种情况:在某个类中定义的各个域只会被初始化一次,默认情况下我们会采用以下方法进行处理:

class Email

fun loadEmails(person:Person):List{

println("load Emails")

return listOf()

}

class Person(val name:String){

private var _emails:List? = null

val emails: List

get() {

if (_emails == null){

_emails = loadEmails(this)

}

return _emails!!

}

}

Kotlin采用了更为优雅的方案以解决之前的问题为目标,并通过延迟计算的方式实现了以下代码:val emails = self.lazy { loadEmails(self) }。

lazy是默认线程安全的

一个类实现了以下方法,便可以作为代理属性所指向的类。

operator fun getValue

operator fun setValue

Map中默认实现了上述方法,可以直接作为代理属性。

chapter8

1.函数类型

该系统支持多种数据类型的输入与处理机制(support multiple types of input and processing methods)。当该系统采用该数据类型的输出结果时,则会生成一个高阶程序(generate a higher-order program),其中被调用的子程序具有独立的功能模块(independent functional modules),因此在实现这些子程序时需要特别注意性能优化方面的问题,并且可以通过嵌套方式处理。

2.incline 函数

运行该函数时会切换到对应函数所在的内存区域完成任务后将返回至原内存区域。而采用内联方式可避免传统的现场保护机制以及频繁切换内存空间的问题。这样做会增加原有函数启动前所需的内存资源消耗。类似于在编译阶段将函数内容展开为等效的宏指令插入到原有代码中以实现相同功能。

3.内联函数use

用于操作可关闭的资源,使用完毕后会自动关闭掉用的资源。

This use operation is an extension operation called on a closable resource, receiving a lambda as its parameter. It invokes the lambda and guarantees that the resource remains closed irrespective of whether the lambda executes successfully or encounters an error.

4.lambda的return

为了避免lambda函数的返回值影响到外层函数的执行结果,请采用label@的方式设定lambda函数的返回范围。其中,label的名字默认与函数名一致,但也允许用户自行指定。

该apply方法直接作用于当前调用对象。在high-order call场景下,应采用@label的方式明确标识This reference以避免混淆。

5.匿名函数

采用匿名函数来代替lambda表达式作为参数时,在return的时候返回的是一个匿名函数,并不会对外层函数产生任何影响。

识别普通函数的语法特征的是:其特点在于没有明确的函数名称和参数类型说明。

chapter9

1.泛型(generic type)

Java采用了Raw类型的机制:定义了泛型机制,在引用时无需明确指定具体类型。这种设计是为了兼容Java 1.5之前的版本无法支持泛型的情况。

但kotlin中不支持raw type,因为kotlin生而支持泛型。

在Kotlin语言中对泛型返回值的限定措施,在编程中被广泛应用于确保类型系统的完整性与安全性。该机制允许针对类型T实施多种限定措施,并通过where关键字可实现相应的类型限定措施。

2.类型擦除

在运行期间, 用于判断value是否为形如 List 的判断语句会在运行时抛出错误, 因此 J 搬动机环境中的 类型信息被隐去, 使得无法确定 List 中所存储的对象类型的详细信息.

Considering that removing generic type information can offer advantages, it is worth noting that the total memory usage of your application decreases, as fewer type-related data are stored in memory.

该incline函数通过reify关键字实现,在运行时避免了类型擦除问题。需要注意的是这类Kotlin方法可以直接被Java调用。

marking a function as inline only yields performance advantages when lambda parameters exist and these lambdas can be integrated seamlessly with the function body. However, in this instance, it is not appropriate to proceed in that manner.

defining the function in an in-line manner, primarily due to performance considerations; rather than that, you are implementing it with the aim of facilitating the use of reified type parameters.

3.协变类

该协变类(如我们所举的例子中所示):假设A是B的一个子类型,则在该协变体系下,相应地,Producer也将成为属于Проducer的一个子类型。

反协变:如果A是B的子类型,Producer将会是Producer的子类型。

该out关键字符决定了生产方与返回类型的关联性;而该in关键字符则决定了消费者方与参数类型的反向关联性。

需要注意的是,在构造函数参数中,并非所有的参数都位于neither the 'in' nor 'out'位置;特别地,在一个私有类型的属性情况下,则可以选择同时采用out和in两种方式来实现功能。

4.泛型中的*

*等同于in nothing意味着,该泛型对象只能作为生产者。

chapter10

1.@Deprecate

Kotlin通过@Deprecate标签包含了replacewith的功能。开发人员被建议利用IDE提供的提示来自动地将过时的功能替换成新的功能。

2.双冒号

::代表地获取类方法的引用(KFunction),不可以代表地获取字段的引用(KProperty)。

我的笔记一直在更新中,并且书籍的下载地址已发布在GitHub仓库中:https://github.com/panxl6/Kotlin-in-action/tree/master/ebook

(包含pdf、mobi等多种版本)

全部评论 (0)

还没有任何评论哟~