写在前面,虽然kotlin做为一门现代语言,但在kotliln上可以看到很多其他语言的影子,简单来说就是采用了长处。简单的使用了kotlin后,尤其是anko相关插件,我习惯觉得kotlin更像是一个提供了高级封装的语言,让你的开发更加便捷。但这个体会只能说是比较粗陋的,毕竟我并没有用kotlin进行实际的开发。
1、类
默认任何类都是基础继承自Any(这个与java中的Object类似)。所有的类默认都是不可继承的(final),所以我们只能继承那些明确声明open或者abstract的类,继承符号用:表示
open class Person(name: String)
class Student(name: String, surname:String) : Person(name)
它有一个默认唯一的构造器。
而构造函数的函数体你可以写在init块中:
Student(name: String, surname: String) {
init{
...
}
}
如果有多个构造器,通过constructor关键字,构造体直接跟在后面
class Student {
public constructor(name:String){ …… }
public constructor(name:String, age: Int)
}
2、函数
函数(在Java中叫方法)可以使用fun关键字就可以定义:
fun onCreate(savedInstanceState:Bundle?) {
}
如果你没有指定它的返回值,它就会返回Unit,与Java中的void类似,但是Unit是一个真正的对象。你当然也可以指定任何其它的返回类型:
fun add(x: Int, y: Int) : Int {
return x + y
}
此外,如你所见
Kotlin
中的参数与
Java
中有些不同,先写参数的名字再写它的类型。此外,
我们也可以给参数指定一个默认值使得它们变得可选。
fun add(x: Int, y: Int=5) : Int {
return x + y
}
调用时,可以是add(3 , 6), 也可以是add(3)。
这非常有用的,再举个例子,toast通知,我们可以这么封装
fun toast(message: String, length: Int = Toast.LENGTH_SHORT) {
Toast.makeText(this, message, length).show()
}
调用
toast("Hello")
toast("Hello", Toast.LENGTH_SHORT)
在java中我的习惯是创建一个基础方法让其他方法都来调用这个方法,而其他方法都带有一个默认值,而在kotlin中却只需要定义一个函数就可以了。有没有觉得代码清晰了许多(一胖毁所有?_?)。
看到这里,大家应该还有一个发现,在kotlin中每一行结束是不需要分号的,但也可以加。
3、基本类型
在说基本类型,先简单提下变量这个概念。
变量可以很简单地定义成可变(var)和不可变(val)的变量。
声明变量时,可以不指定其类型的
val s = "Example" // A String
val i = 23 // An Int
是不是看到某个语言的影子了,但声明后是不能改变其类型的哦。
回到基本类型,像integer,float或者boolean等类型仍然存在,但是全部都会作为对象存在的。所以别在kotlin中敲什么int,float了,要变成Int,Float。下面是声明时指定类型:
val i:Int=7
val d: Double = i.toDouble()
注意,kotlin中基本类型还有一些与java不一样的地方,如数字类型中不会自动转型、char类型不能直接当int型处理、一个字符串类型可以被当成数组处理等。
4、属性
属性与Java中的字段是相同的,但是更加强大。属性做的事情是字段加上getter加上setter。也许你会说直接pubilce不就可以了嘞,但是为了安全性的要求,我们应该只能通过提供接口来修改属性,而不是直接访问属性。
在java中
public class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
//调用,如果所需属性多的话,这代码就要长长长的了
Person person = new Person();
person.setName("name");
String name = person.getName();
但在Kotlin中,只需要一个属性就可以了:
publicclass Person {
var name: String = ""
}
//调用
valperson = Person()
person.name ="name"
val name= person.name
是不是方便了很多。
也许你会说,kotlin不是直接访问了属性么?其实不是,这只是表面上的,编译器还会“翻译”代码的。编译器会直接链接到它原始的getter/setter方法。此外,当我们直接访问属性的时候不会有性能开销。
如果需要在getter和setter中访问这个属性自身的值,它需要创建一个backingfield。可以使用field这个预留字段来访问,它会被编译器找到正在使用的并自动创建。需要注意的是,如果我们直接调用了属性,那我们会使用setter和getter而不是直接访问这个属性。backingfield只能在属性访问器内访问。
public class Person {
var name: String = ”default"
get() = field.toUpperCase()
set(value){ //value相当于我们定义的变量名,可修改
field = "Name: $value"
}
}
看到上面的属性访问,我们一般会联想到实体类,在kotlin有一个更快捷的方法
data class Student(
var id: Long = -1,
var name: String = "",
var age: Int = -1)
data 表明这是一个数据类。kotlin中的数据类会自动生成所有属性及其访问器,还包括了其他的一些方法,如
toString()。
5、可见性修饰符不像java中分得那么细,有类修饰符、变量修饰符等。kotlin就四种(但也不是说所有范围都可以用)。
Private
它表示它只能被自己所在的文件可见。所以如果我们给一个类声明为private,我们就不能在定义这个类之外的文件中使用它。如果我们在一个类里面使用了private修饰符,那访问权限就被限制在这个类里面了。甚至是继承这个类的子类也不能使用它。
protected
这个修饰符只能被用在类或者接口中的成员上。定义在一个成员中:它可以被成员自己和继承它的成员可见。(不能用来修饰类或接口)
internal
如果是一个定义为internal的包成员的话,对所在的整个module可见。如果它是一个其它领域的成员,它就需要依赖那个领域的可见性了。比如,如果我们写了一个private类,那么它的internal修饰的函数的可见性就会限制与它所在的这个类的可见性。
这里的module可以直接理解为android studio 中module,你可以在主工程下创建一个module,再创建相关的类,就可以很好的理解这几个概念了。
Public
默认的修饰符,成员在任何地方被修饰为public,只限制于它的领域。一个定义为public的成员被包含在一个private修饰的类中,这个成员在这个类以外也是不可见的。
6、扩展函数
扩展函数是指在一个类上增加一种新的行为(函数),甚至我们没有这个类代码的访问权限。在Java中,通常会实现很多带有static方法的工具类提供着各种方法。Kotlin中扩展函数的一个优势是我们不需要在调用方法的时候把整个对象当作参数传入。扩展函数表现得就像是属于这个类的一样,而且我们可以使用this关键字和调用所有public方法。
举个例子,我们可以创建一个toast函数,这个函数不需要传入任何context,它可以被任何Context或者它的子类调用,比如Activity或者Service:
fun Context.toast(message:CharSequence,duration: Int= Toast.LENGTH_SHORT){
Toast.makeText(this,message, duration).show()
}
这个方法可以在Activity内部直接调用:
toast("Hello world!")
toast("Hello world!", Toast.LENGTH_LONG)
是不是,十分强大。
但在实际测试中,却有个小坑,如果你在A中定义,在B中想调用,却是有个条件A是kotlin的file文件,而不是class文件,新建kotlin时可别选错了。(也许是我操作错了??)
关于这个我暂时先这样理解:上面我们也提到工具类,一类归一类,好的习惯是统一定义到一个类中,而扩展函数也是一样的,如果你在Activity1中定义了一个activity的方法,而在Activity2中定义了另一个activity的方法,那到时管理就很麻烦了。
回到主题:
扩展函数也可以是一个属性,所以我们可以通过相似的方法来扩展属性。
比如,我们可以用此方法来设置View的padLeft属性:
//使用扩展属性(extension property)
var View.padLeft: Int
set(value){
setPadding(value, paddingTop, paddingRight, paddingBottom)
}
get(){
return paddingLeft
}
有一点要注意:由于扩展属性实际上是不会向类添加新的成员,所以对于扩展属性不允许存在初始化器。从某个角度上来说,扩展属性是不存在的。扩展属性的行为只能通过明确给定的取值方法与设值方法来定义,也就意味着扩展属性只能被声明为
var
而不能被声明为
val。
最后关于扩展函数要提到的是:Anko库
Anko是JetBrains开发的一个强大的库s(不只一个,基础库、数据库等等)。它主要的目的是用来替代以前XML的方式来使用代码生成UI布局。但Anko还包含了很多的非常有帮助的函数和属性来避免让你写很多的模版代码。同时增强性日志输出、数据库封装都有。所以像举的例子toast,都给你封装好了。所以说如果你要进行kotlin开发,你离不开anko.
7、表达式
常见的表达式有if/if else 、when 、for等。这里挑几个与java较大不同的来讲。
When
when表达式与Java中的switch/case类似,但是要强大much,它的参数可以是任何类型,并且分支也可以是一个条件。同时它有返回值。
对于默认的选项,我们可以增加一个else分支,它会在前面没有任何条件匹配时再执行。条件匹配成功后执行的代码也可以是代码块:
when(x){
1 -> print("x == 1")
2 -> print("x == 2")
else -> {
print("x is neither 1 nor 2")
}
}
还有
val res = when{ //表达式
x in 1..10 -> "small" //范围值表示
s.contains("hello") -> "it's a welcome kotlin!"
v is ViewGroup -> "child count: ${v.getChildCount()}"
else -> "nothing"
}
Ranges和for表达式
ranges可以很好的应用在for循环里,这里先讲ranges
range表达式使用一个..操作符,如0..10,表示0到10,反之如果10..0表示10到0,这是一个很强大的功能,甚至能自定义步数,如0..10step2,表示0到10间,以2递增.
所以将ranges和for很好的结合在一起就成了
for(xin 0..10) {
print(x)
}
或者
for(x in 10 downTo 2 step2) {
print(x)
}
表示10到2,以2递减。
8、异步
我们都知道,HTTP请求不被允许在主线程中执行,否则会抛出异常。
Anko提供了非常简单的DSL来处理异步任务,它满足大部分的需求。它提供了一个基本的async函数用于在其它线程执行代码,也可以选择通过调用uiThread的方式回到主线程。在子线程中执行请求如下这么简单:
Async(){
Request(url).run()
uiThread { longToast("Request performed") }
}
是的,就是这么简单,比起什么AsyncTask是不是简捷多了。
UIThread还有一个很强大的功能,如果它是被一个Activity调用的,那么如果activity.isFinishing()返回true,则uiThread不会执行,这样就不会在Activity销毁的时候遇到崩溃的情况了。
有一个值得提的,如果想学好kotlin,可以多看看anko的源码,如上面的run就是了。
还有一个anko没有提供强大的网络库,毕竟现在成熟的库非常多,而kotlin的一大特性便是可以与java无缝结合了,在kotlin调用个java库不是什么问题。(但是别在kotlin文件中new 呀new就是了)。
9、数据库
当我们使用SqliteOpenHelper,我们需要去调用getReadableDatabase()或者getWritableDatabase(),接着进行数据库的处理。最后,我们不能忘记调用close()。但使用ManagedSqliteOpenHelper我们只需要:
forecastDbHelper.use {
...
}
查看源码:
publicfun use(f: SQLiteDatabase.()-> T): T {
try {
return openDatabase().f()
} finally {
closeDatabase()
}
}
是的,就是这么赤祼祼的强大。
后面会提到写一个简单的demo。这里先提到遇过两个小问题:
1、kotlin和anko的版本一定要统一哈,ext,不用多说了吧。尤其是anko的库,比竟有很多个。
2、操作数据库时如果没编译成功,明明有函数,就是指不过去,一是手动Import,二是升级,anko版本,此外anko库没有向下兼容,升级后极个别函数不识别。