“Kotlin in Action”读书笔记

来源:互联网 发布:遗传算法图像分割 编辑:程序博客网 时间:2024/05/17 08:54



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){

        is Num -> e.value  //此处无需进行显示的进行类型转换就可以使用对应类型的属性

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

        else -> throw IllegalArgumentException("Unknown expression")

    }


2.range

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

“..” 关键字除了可用于字符和数字之外,但不仅限于,可以通过对Comparable接口的实现,来将我们自定义的对象用”..”关键字连接

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


3.异常

kotlin不再对异常进行checked unchecked的区分,意味着我们可以不对任何异常进行捕捉,也可以对运行时异常进行捕捉。现实中,有时对运行时异常的捕捉来的更有意义。例如

fun readNumber(reader: BufferedReader):Int?{

    try {

        val lin = reader.readLine()

        return Integer.parseInt(lin)

    }catch (e: NumberFormatException){ //此处的Runtime Exception kotlinjava一致可以不进行捕    //  捉,但具有更强的现实意义

        return null

    }finally {

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

    }

}


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

println(readNumber(reader))



Chapter 3


1.集合

setOf ->  LinkedHashSet

listOf ->  ArrayList

map  ->   HashMap

以上关键字用于快速创建集合,以及他们创建的对象对应的javaClass的类型


2.顶级方法和属性

拜托了各种util类的束缚,在一个文件中非类的范围内定义的方法和属性,可以被全局访问。

@file:JvmName("StringFunctions") 

顶级文件会生成对应名字相同的类,而方法将会变为该类中的静态方法,@file:JvmName用于设定生成java类的类名



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

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

扩展函数同静态方法一样,是静态绑定。所以即使父子类做了同名扩展,执行的扩展函数为声明时类型对应的方法。


4.数组解包unpack 

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


5.中缀函数infix

在函数声明前加上infix关键字可以在执行方法时,省去”.””()“而改用” “(空格)代替


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












Chapter 4

1.类的基本特性

使用”:“替代java中的implementsextends

kotlinoverride关键字是强制要使用的

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

kotlin中的类和方法默认是final的,如果需要被inheritoverride需要被声明为open

方法被继承后会默认自动转换为open,如果需要它无法被override需要把它声明为final("override" without "final" implies being "open".` )

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

2.可见性问题

kotlin中有和publicprivateprotected一样的可见性修饰符,但kotlin中默认为public

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

kotlin中新访问属性internal,作为module访问权限(Amoduleis a set of Kotlin files compiled together. It may be an IntelliJ IDEA module, an Eclipse project, a Maven or Gradle project, or a set of files compiled with an invocation of the Ant task. 

kotlinprotected修饰符,只能被子类可见,这不同于java中额外的同一package访问权限


3.内部类问题

kotlin中没有任何关键字修饰的内部类,等同于Java中的static class,不持有外部类的引用

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

class Outer {
inner class Inner { 

fun getOuterReference(): Outer = this@Outer } 

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


4.sealed关键字

when关键字else可以通过该关键字来去除,具体方法是:将某个类A的子类全部声明为其内部类,并将类Asealed修饰,该方式同时可以使类A无法在除了自己类内部和同一文件以外(1.1以前会更苛刻,只能在类内部)的地方声明子类

A sealed class can have subclasses, but all of them must be declared in the same file as the sealed class itself. (Before Kotlin 1.1, the rules were even more strict: classes had to be nested inside the declaration of the sealed class).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()声明方法,避免实现类中的强制override语法。

interface User{

    val name: String

    get() = "111"

    val nickname : String

    get() = "nick" + name

}

class NetUser():User{

}


7.类中field的访问权限

set()和get()方法默认的访问权限与该方法对应的field的权限一直,但允许对这些方法单独设定其访问权限

class LengthCounter {

     var counter: Int = 0

        private set

        public get

}



8.”==““equals” 

kotlin中,使用“==”会默认调用类的equals方法进行比较判断,如果需要对reference进行比较的话则需要用“===”符号。这与java的区别较为明显

插入set时的情况

at first their hash codes are compared, and then, only if they’re equal, the actual values are compared. 


9.data class 数据类

data class 声明会使类自动生成 toString(),hashCode(),equals()方法,这三个方法的实现都会和类中主要构造器中携带的field相关。copy()也是该声明会自动生成的方法,默认不传参数情况下会生成源对象的副本,传参数可以生成修改调用copy()对象对应字段的值的副本。


10.代理类“by”

像代理模式一样,java种做法是需要实现interface的所有并持有一个实现interface的对象,由该对象处理实现接口的对应所有方法。无疑这种方式会产生大量冗余的代码,kotlin提供了一种优雅的方式来处理这种情况,这种方式可以让我们有选择性地选择override方法,更不需要override全部方法。如下:

interface A{

fun a()

}

class B : A{

overriden fun a(){

println(“aaa”)

}

}

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



11.objectthe final big part of Kotlin’s class story

object关键字修饰的类其对象的生成是在类定义的同时,这种情况导致该类没必要也不允许定义构造器(neither primary nor secondary),objectkotlin实现单例模式的手段。(An “object” in Kotlin is compiled as a class with a static field holding its single instance


companion:

包级别的函数一定程度上可以作为Javautil类中static方法的替代,但有时我们遇到如下情况:

只希望函数被该类访问,而不希望被包级别的函数访问。工厂方法的实现可以依靠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之后,会有中间持有结果collection的产生,而使用asSequence则会避免中间量的产生。所以当数据量较大时,应使用asSequence

chapter6


1.nullable

方法中未声明为可null类型的变量参数,当向其中传入null值时,将会在编译时直接报错,而不像java中变成运行时异常。

null参数不可为不可null参数赋值,即使可null参数已被赋值为非null状态。

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

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

2.as?关键字 

3.!!关键字

Essentially, you’re telling the compiler, "I know the value isn’tnull, and I’m ready for an exception if it turns out I’m wrong. 


4.let

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

pastedGraphic.png 

5.lateinit

标志类中的该字段不用在生命的同时初始化,其值的赋值可以依赖后续方法的调用。在依赖注入的情况下,会比较常用这种手段。


6.泛型中的null问题

fun <T>  foo (t : T)该情况中t实际上可以传递可null参数因为<T>等同于<T: Any?>,如果不希望支持可null参数,则需要将<T>更改为<T:Any>


7.Javakotlin联合的null  

java 类型可以被为kotlinnullable赋值也可以为kotlinnon-null赋值


8.原始类型

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

kotlin中的Int在大部分情况下会被编译成java的原始类型int,以下两种情况是意外:

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

2)另一种被编译成包装器的情况是,变量被声明为nullable类型,因为原始类型无法接受 null引用


kotlin中不存在隐式的向上转型或向下转型,即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

CollectionMutableCollection分别表示该类型所指向的集合是只读还是可变的。

如果函数声明时明确了要使用MutableCollection作为参数,那么传入了Colletion类型的引用(即使知道它所指向的对象是可变的)也不可以。不过,反之却是可行的。


javaCollection接口部分为可变和不可变,Java声明传入Java.util.Collection的函数被kotlin调用时,kotlin传入的类型没必要进行区分,kotlin.collections.Collectionkotlin.collections.MutableCollection皆可以。

Collection接口分为两类,相应的kotlin中对应的实现Collection接口的数据接口,setlistmap也会被分成两种实现。


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

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

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


Array<Int>生成的是Java中的Integer[],要生成原始类型的数组要使用IntArray

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



Chapter7


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

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

kotlin中允许我们通过operator+扩展方法,重载+-\*%这些运算符,实现非原始类型之间的运算。 


位运算符不可以重载













2.plus assignPlus


若变量声明为var,当执行a+=b时,两者会产生冲突,编译器 不知道调用哪一个方法而报错。

解决上述问题需要将变量声明为val,或者显式的用方法名调用两种方法。


+=在操作可变collection(MutableColleciton)时会就地改变它的状态,在操作不可变Collection时会返回一个原数据的副本。




3.结构声明 

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


4.代理属性 delegate property

用于延迟加载:

试想一种情况:类中的域只会初始化一次,但需要在使用该域时延迟加载,默认我们以这样实现:

class Email


fun loadEmails(person:Person):List<Email>{

    println("load Emails")

    return listOf()

}


class Person(val name:String){

    private var _emails:List<Email>? = null


    val emails: List<Email>

    get() {

        if (_emails == null){

            _emails = loadEmails(this)           

        }

        return _emails!!

    }

    }


Kotlin提供了更优雅的方式达到上述的目的,val emails by lazy {loadEmails(this)}

lazy是默认线程安全的

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

operator fun getValue

operator fun setValue

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

chapter8

1.函数类型

函数类型既可作为参数类型,也可以作为返回值类型。在函数类型作为返回类型后,会形成高阶函数(high-order function),高阶函数的闭包(访问上层函数的作用域的内层函数就是闭包)效率问题,可以使用内联函数解决。


2.incline 函数

执行函数时,会转去对应函数的内存空间执行完成之后再转回之前的内存空间,而内联函数避免了这种现场保护和转来转去的麻烦,但是会增加原有函数执行前内存空间的开销,相当于在编译时生成类似于宏的形式将函数内容替换至原有内存空间,这种方式增大空间开销减小时间开销。(内联函数,空间换时间,适合于小内存空间的函数。) 

3.内联函数use

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

The use function is an extension function called on a closable resource; it receives a lambda as a parameter. The function calls the lambda and ensures that the resource is closed, regardless of whether the lambda completes normally or throws an exception. 


4.lambdareturn

为了防止lambdareturn导致outer函数的返回,要是用label@的方式,制定lambdareturn范围。label的名称默认为函数名,也可以自定义名称。



同时apply方法操作的是调用对象本身,如果存在高阶调用,this引用也需要@label的方法加以区分。


5.匿名函数

使用匿名函数作为参数替代lambda表达式时,使用return返回的时匿名函数,不会影响外层函数。

匿名函数区分普通函数的语法是:匿名函数去掉了函数名和参数的类型名


chapter9


1.泛型(generic type

java支持raw type:声明了泛型,但在引用了时不指明泛型对应的具体类。这种支持是为了兼容java1.5以前不支持泛型的版本。

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


kotlin中泛型返回值限制,支持对T的多重限制,可以使用where关键字来实现。



2.类型擦除

在运行时,value is List<String>这样的判断语句会被报错,因为jvm运行时类型擦除的缘故,无法对List中持有的对象类型进行判断。

Note that erasing generic type information has its benefits: the overall amount of memory used by your application is smaller, because less type information needs to be saved in memory. 

incline函数使用reified关键字,可以免除runtime时的类型擦除。(但注意这样的kotlin方法无妨从Java调用)

marking a function as inline only has performance benefits when the function has lambda parameters and the lambdas are inlined together with the function. But in this case, you aren’t

marking the function as inline for performance reasons ; instead, you’re doing it to enable the use of reified type parameters. 




3.协变类

一个协变类(我们拿Producer<T>举例):如果AB的子类型,Producer<A>将会是Producer<B>的子类型。

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

out关键字用于生产者即返回类型实现协变,in关键字用于消费者参数类型实现反协变。

Note that constructor parameters are in neither the   in  nor  out  position.但如果构造函数中的属性为private类型,则使用out in 皆可以。


4.泛型中的*

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

chapter10


1.@Deprecate

kotlin@Deprecate提供了replacewith的属性,可以使用IDE给出的提示将过期函数自动替换为新的函数。

2.双冒号

:: 双冒号可以获取方法的引用(KFunction),也不可以获得属性的引用(KProperty)。

 

笔记持续更新中 书的下载地址https://github.com/panxl6/Kotlin-in-action/tree/master/ebook

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




原创粉丝点击