介绍Kotlin
- 编写代码量少
- 更加安全:Kotlin编译时期就处理了各种null的情况,避免了执行时异常。如果一个对象可以是null,则我们需要明确地指定它,然后在使用它之前检查它是否是null
- 它是函数式的:Kotlin是基于面向对象的语言,它使用了很多函数式编程的概念,比如,使用lambda表达式来更方便地解决问题。其中一个很棒的特性就是Collections的处理方式
- 它可以扩展函数:可以扩展类的更多的特性,甚至我们没有权限去访问这个类中的代码
- 它是高度互操作性的:你可以继续使用所有的你用Java写的代码和库,因为两个语言之间的互操作性是完美的。可以在一个项目中使用Kotlin和Java两种语言混合编程
特性
Expresiveness 可读性
POJO
1
2
3
4
5
6
7
8
9
10
11
12public class Artist {
private long id;
private String name;
private String url;
private String mbid;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
···Kotlin中创建数据类Artist.kt,自动生成所有属性和访问器
1
data class Artist(var id: Long, var name: String, var url: String, var mbid: String)
空安全
1 | //编译不通过,非空类不能为null |
扩展方法
1 | val fragment= Fragment() |
Lambda
1 | textView.setOnClickListener { toast("Kotlin") } |
创建一个项目
Project/build.gradle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27buildscript {
ext.kotlin_version = '1.2.30'
ext.support_version = '26.1.0'
ext.anko_version = '0.10.4'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}app/build.gradle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 26
defaultConfig {
applicationId "com.willkernel.app.kotlindemo"
minSdkVersion 23
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
androidExtensions {
experimental = true
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "org.jetbrains.anko:anko-common:$anko_version"
implementation "org.jetbrains.anko:anko-sqlite:$anko_version"
implementation "org.jetbrains.anko:anko-coroutines:$anko_version"
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}转换java到kotlin
导入布局属性
import kotlinx.android.synthetic.main.activity_main.*
1
2textView.setText(R.string.app_name)
textView.text = getString(R.string.app_name)定义类,有一个默认唯一的构造器,如果没有方法,只要名称,变量,可以省略大括号
1
2
3
4
5
6class Person(var name: String, var username: String) {
/**构造函数函数体*/
init {
}
}任何类继承自Any(类似Object)可以继承其他类,所有类默认不可继承(final),所有只能继承那些声明open或者abstract的类,指定父类中的参数
1
2open class Animal(name: String)
class Person(name: String, surname: String) : Animal(name)
函数
fun
关键字定义函数,没有指定返回值时,返回Unit类似void,但是Unit是一个对象1
2
3
4
5
6
7
8fun onCreate(savedInstanceState: Bundle?) {
}
fun add(x: Int, y: Int) : Int {
return x + y
}
fun add(x: Int,y: Int) : Int = x + y构造方法和函数参数
指定参数默认值
1
2
3
4
5
6
7fun toast(message: String, length: Int = Toast.LENGTH_SHORT) {
Toast.makeText(this, message, length).show()
}
避免重载函数,第二个参数可不传
toast("Hello")
toast("Hello", Toast.LENGTH_LONG)三个参数,避免重载函数,string引用模板
[$className] $message
,${user.name}
1
2
3
4
5
6
7private fun toast(message: CharSequence, tag: String = "MainActivity", duration: Int = Toast.LENGTH_SHORT) {
Toast.makeText(this, "[$tag] $message", duration).show()
}
toast("Hello")
toast("Hello", "MyTag")
toast("Hello", "MyTag", Toast.LENGTH_SHORT)
创建一个layout
列表使用recyclerview
1
2
3
4
5
6implementation "com.android.support:recyclerview-v7:$support_version"
val forcastList = findViewById(R.id.recyclerView) as RecyclerView
//通过属性赋值的方式设置LayoutManager(对象实例化没有使用new关键字),而不是通过setter
forcastList.layoutManager = LinearLayoutManager(this)Recycler Adapter
1
2
3
4
5
6
7
8
9
10
11
12
13class ForcastListAdapter(val items: List<String>) : RecyclerView.Adapter<ForcastListAdapter.VH>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
return VH(TextView(parent.context))
}
override fun getItemCount(): Int = items.size
override fun onBindViewHolder(holder: VH, position: Int) {
holder.textView.text = items[position]
}
class VH(val textView: TextView) : RecyclerView.ViewHolder(textView)创建数据,设置Adapter,listOf 创建一个常量的List,它接收一个任何类型的 vararg (可变长的参数),它会自动推断出结果的类型。还有很多其它的函数可以选择,比如 setOf , arrayListOf 或者 hashSetOf
1
2
3
4
5
6
7
8
9
10//val forecastList = findViewById<RecyclerView>(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(this)
val items = listOf("Mon 6/23 - Sunny - 31/17",
"Tue 6/24 - Foggy - 21/8",
"Wed 6/25 - Cloudy - 22/17",
"Thurs 6/26 - Rainy - 18/11",
"Fri 6/27 - Foggy - 21/10",
"Sat 6/28 - TRAPPED IN WEATHERSTATION - 23/18",
"Sun 6/29 - Sunny - 20/7")
recyclerView.adapter = ForcastListAdapter(items)
变量和属性
基本类型
数字类型不会自动转型,做一个明确的类型转换
1
2
3
4
5val i:Int=7;
val d:Double=i.toDouble()
Log.e(tag, d.toString())
E/MainActivity: 7.0字符char的数字值不能直接使用,需要转换为一个数字
1
2
3
4
5val c='c'
val i2:Int=c.toInt()
Log.e(tag,i2.toString())
E/MainActivity: 99仅适用Int Long的按位运算,包括
1
2
3
4
5
6
7shl(bits) – signed shift left (Java's <<)
shr(bits) – signed shift right (Java's >>)
ushr(bits) – unsigned shift right (Java's >>>)
and(bits) – bitwise and
or(bits) – bitwise or
xor(bits) – bitwise xor
inv() – bitwise inversion- 示例
1
2
3
4
5
6
7
8
9val FLAG1 = 8L
val FLAG2 = 16L
//按位运算,Int Long可用
val bitwiseOr = FLAG1 or FLAG2
val bitwiseAnd = FLAG1 and FLAG2
Log.e(tag, bitwiseOr.toString())
Log.e(tag, bitwiseAnd.toString())
E/MainActivity: 24
E/MainActivity: 0
- 示例
string字符串的访问
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16//string 可以像数组一样访问
val s = "Example"
val c1 = s[2]
Log.e(tag, c1.toString())
for (c in s) {
Log.e(tag, "item =$c")
}
E/MainActivity: a
E/MainActivity: item =E
E/MainActivity: item =x
E/MainActivity: item =a
E/MainActivity: item =m
E/MainActivity: item =p
E/MainActivity: item =l
E/MainActivity: item =e
变量
- 变量可以很简单地定义成可变( var )和不可变( val )的变量,不可变对象也可以说是线程安全的,因为它们无法去改变,也不需要去定义访问控制,因为所有线程访问到的对象都是同一个,尽量使用
val
,自动从赋值语句判断变量类型1
2
3
4
5
6
7
8
9
10
11val actionBar = supportActionBar
Log.e(tag, actionBar.toString())
val a: Any = 23
Log.e(tag, a.toString())
val c: Context = this
Log.e(tag, c.toString())
E/MainActivity: android.support.v7.app.WindowDecorActionBar@2511c44
E/MainActivity: 23
E/MainActivity: com.willkernel.app.kotlindemo.MainActivity@b16dc5a
属性
- 当操作Java代码的时候,Kotlin将允许使用属性的语法去访问在Java文件中定义的getter/setter方法。编译器会直接链接到它原始的getter/setter方法。所以当我们直接访问属性的时候不会有性能开销
- 在getter和setter中访问这个属性自身的值,它需要创建一个 backingfield 。可以使用 field 这个预留字段来访问,它会被编译器找到正在使用的并自动创建。需要注意的是,如果我们直接调用了属性,那我们会使用setter和getter而不是直接访问这个属性。 backing field 只能在属性访问器内访问
1
2
3
4
5var nick: String = ""
get() = field.toUpperCase()
set(value) {
field = "Name: $nick"
}