基本用法
1.常量和变量
使用val关键字声明一个常量(只读,不可修改),使用var关键字声明一个变量,下面是具体用法:
1. fun test() {
2. //使用val关键字声明一个常量(只读),声明常量时必须初始化
3. val a: Int = 1 //显式指定常量的类型
4. val b = 2 //自动推断类型
5. val c: Int //声明一个不初始化的常量,必须显式指定类型
6. // b = 3 //常量值不可修改,这句代码会报错
7.
8. //a = 3 //不可以修改常量的值,此句代码会报错
9.
10. //使用var关键字声明一个变量,变量的值可以修改
11. var year: Int = 2016 //显式指定变量的类型
12. var month = 5 //自动推断变量类型
13. var day: Int //声明一个不初始化的变量,必须显式指定类型
14. month = 6 //变量值可以被修改
15. }
基本类型
对于java中我们都很清楚基本类型有 byte、short、int、long、double、float、boolean、char 等,对于Kotlin中当然也有与其对应的,不过它们更像是java中的包装类 比如int->Integer ,Kotlin中是Int
下面是对应关系(首字母变大写)
- int->Int
- double->Double
- long->Long
- double->Double
- float->Float
- boolean->Boolean
- char->Char
Kotlin中使用接近于Java的方式(内置类型)来表示数字,但是又不完全相同,
比如没有隐式转换!Kotlin中数字相关的内置类型如下:
需要注意几点:
● 1.没有自动向上转型,比如Int转Long,需要自己调toXxx方法转
● 2.Long类型结尾必须为大写的L,不能为小写,比如1024L
● 3.字符Char不是Number,用单引号来声明,比如'c',不能像Java一样直接拿来当数字使,
如果你想把Char的值给Int,需要调toInt()方法
● 4.Boolean的值为true或false
● 5.Kotlin不支持8进制,十六进制0x开头,二进制0b开头
● 6.位运算符,Java中的与或运算符用:|和&,kotlin中使用or和and关键字来替代
其他运算符也有分别的关键字替代:shl(有符号左移),shr(有符号右移),ushr(无符号右移)
,xor(按位异或),inv(按位取反)
========================================================================================2、函数(方法)的使用
定义函数使用 fun 关键字,如下代码所示:
1. fun add(a: Int, b: Int): Int {
2. return a + b
3. }
函数add有两个Int型的参数,冒号后跟的是函数的返回值,一条代码语句的末尾不用加分号,当然加上分号也没有问题。
上面的add函数还可以简写成如下形式:
1. fun add(a: Int, b: Int) = a + b;
没有显式指定函数的返回值,会自动推断函数的返回值。
如果一个函数没有返回值,可以写成如下两种形式:
1. //没有返回值的函数,显式指定Unit为返回值
2. fun showAddResult(a: Int, b: Int): Unit {
3. println(a + b)
4. }
5.
6. //没有返回值的函数,省略Unit的写法
7. fun showAddResult2(a: Int, b: Int) {
8. println(a + b)
9. }
=========================================================================
4、字符串模板
Kotlin允许在字符串中嵌入变量和表达式,只用在字符串内用 $ 符号开头,随后跟上输出变量的变量名即可,例如:
val name = "Bob"
println("My name is ${name}") //打印"My name is Bob"
val a = 10
val b = 20
println("The sum is ${a+b}") //打印"The sum is 30"
你也可以用下面的表达式:
val apples = 4
val bananas = 3
println("I have $apples apples and " + (apples + bananas) + " fruits.") // Java-esque
println("I have $apples apples and ${apples+bananas} fruits.") // Kotlin
//args 表示数组名称 Array表示字符串类型的数组 ${args[0]} 表示数组中第0个元素的值
1. //字符串模板的用法 t?
2. fun stringTempl(args: Array) {
3. if(args.size > 0)
4. println("args[0] = ${args[0]}")
5. }
6.
7. //main方法是整个程序的入口
8. fun main(args: Array){
9. var arr = arrayOf("hello", "world")
10. stringTempl(arr)
11. }
上面的代码执行后,在控制台打印如下内容:
我们来检查我们是否给 main 函数传递了参数。先来判断这个字符串数组是不是空,如果不为空,我们把第一个字符串分配给 name 变量。Kotlin 里有个 val 类型的声明方法,类似 Java 里的 final,也就是常量。
fun main(args: Array) {
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) {.
val name = if (args.isNotEmpty()) {
args[0]
} else {
"World"
}
println("Hello, $name!")
}
我们可以把上面的代码用一行来书写,看起来有点像 Java 里的三目运算符。移除掉那些大括号后,看起相当漂亮:
val name = if (args.isNotEmpty()) args[0] else "World"
然后kotlin还支持字符串遍历
======================================================================
5、条件表达式
区间表达式
你可能在其他的语言里见到过这样的表达式。的确, 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 或者你自己的类型。只要符合逻辑就行。
常规的条件表达式可以是这么写的:
1. //常规写法的条件表达式,这里的函数返回值不能省略
2. fun max(a: Int, b: Int): Int {
3. if(a > b)
4. return a
5. else
6. return b
7. }
Kotlin可以简写条件表达式,如下所示:
1. //简写的条件表达式
2. fun max2(a: Int, b: Int) = if(a > b) a else b
==================================================================
6、可空类型
[plain] view plain copy
print?
1. fun nullableTest() {
2. //在变量类型后面加上问号,代表该变量是可空变量
3. var name: String? = "zhangsan"
4. name = null //可以将null赋值给name变量
5. var person: String = "tom"
6. // person = null //这句代码会报错,不可以将null赋值给一个不可空变量
7. }
函数返回值为可空的例子如下代码:
[plain] view plain copy
print?
1. /*
2. 函数返回值为Int?,表示返回值可为空
3. 当参数为空或者为""时,则返回null,否则使用Java中的字符串转整型的方法
4. 这里也体现了kotlin代码和Java代码无缝集成
5. */
6. fun parseInt(s: String): Int? {
7. if(s == null || s == "")
8. return null;
9. return Integer.parseInt(s);
10. }
===============================================================
7、类型检查和自动类型转换
Kotlin中使用is运算符来检查数据类型和做类型转换,如下代码所示:
1. /*
2. 当函数参数为字符串类型时,就返回字符串的长度,否则返回空
3. */
4. fun getStringLength(n: Any): Int? {
5. if(n is String)
6. return n.length //这里会自动将n转化为字符串类型
7. return null
8. }
上面的代码还可以写成:
1. /*
2. 当函数参数为字符串类型时,就返回字符串的长度,否则返回空
3. */
4. fun getStringLength(n: Any): Int? {
5. if(n !is String)
6. return null
7. return n.length //这里会自动将n转化为字符串类型
8. }
======================================================================
8、for循环和while循环
1. //for循环的测试代码
2. fun testFor() {
3. var arr = arrayOf(1, 3, 4, 5, 6)
4. for(i in arr.indices) { //通过索引循环
5. println(arr[i])
6. }
7. for(num in arr) { //直接使用数组中的对象循环
8. println(num)
9. }
10. }
11.
12. //while循环的测试代码
13. fun testWhile() {
14. var i = 0;
15. while(i < 10) {
16. print(" " + i)
17. i++
18. }
19. }
=========================================================================
9、when表达式
when表达式就类似于Java中的switch表达式,如下代码所示:
1. fun main(args: Array) {
2. testCase("hello world")
3. }
4.
5. fun testCase(obj: Any) {
6. when(obj) {
7. is String -> {
8. print("this is string")
9. }
10. is Int -> {
11. print("this is integer")
12. }
13. else -> {
14. print("unkown value")
15. }
16. }
17. }
==========================================================================
10、ranges的使用
(1)使用in操作符检查一个数是否在某个范围内
1. /*
2. 判断分数是否大于等于90,小于等于100
3. */
4. fun isGood(score: Int) {
5. if(score in 90..100) //ranges是闭区间
6. println("very good")
7. else
8. println("not so good")
9. }
(2)检查索引是否越界
1. /*
2. 检查index是否在数组arr的索引范围内
3. */
4. fun checkIndex(index: Int, arr: Array) {
5. if(index in 0..arr.lastIndex) //arr.lastIndex返回的是数组的最后一位的下标
6. println("index in bounds")
7. else
8. println("index out of bounds")
9. }
(3)遍历一个范围
1. for(i in 1..5) {
2. println(i)
3. }
也可以通过in运算符遍历一个集合,如下代码:
1. //in运算符遍历一个字符串数组
2. fun testStr(arr: Array) {
3. for(str in arr)
4. println(str)
5. }
=================================================================================
11、三元运算符
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 操作符 左侧是空,那么他就会返回右侧,否则直接返回左侧的值。
============================================
12、高阶函数
很多语言已经支持了高阶函数,比如 Java 8,但是你并不能用上 Java 8。如果你在用 Java 6 或者 Java 7,下面的例子实现了一个具有过滤功能的函数:
public interface Function {
R call(T t);
}
public static List filter(Collection items, Function f) {
final List filtered = new ArrayList();
for (T item : items) if (f.call(item)) filtered.add(item);
return filtered;
}
filter(numbers, new Function() {
@Override
public Boolean call(Integer value) {
return value % 2 == 0;
}
});
我们首先要声明一个函数接口,接受参数类型为 T,返回类型为 R。我们用接口中的方法遍历操作了目标集合,创建了一个新的列表,把符合条件的过滤了出来。
fun filter(items: Collection, f: (T) -> Boolean): List {
val filtered = arrayListOf()
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
}
=============================================================
13 .内联函数
内联函数和高阶函数经常一起见到。在某些场景下,当你用到泛型的时候,你可以给函数加上inline 关键字。在编译时,它会用 lambda 表达式替换掉整个函数,整个函数的代码会成为内联代码。
如果代码是这样的:
inline fun filter(items: Collection, f: (T) -> Boolean): List {
val filtered = arrayListOf()
for (item in items) if (f(item)) filtered.add(item)
return filtered
}
filter(numbers) { it % 2 == 0 }
由 inline 关键字在编译后会变成如下这样:
val filtered = arrayListOf()
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 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 View.findViewParent(): T? {
var parent = getParent()
while (parent != null && parent !is T) {
parent = parent.getParent()
}
return parent as T // Type cast allowed
}
===========================================================================
14 .函数扩展
函数扩展是 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 customerMapper = // ...
final Function orderFilter = // ...
final Function orderSorter = // ...
final List 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 implements List {
private List delegate;
public MyList(List delegate) {
this.delegate = delegate;
}
// ...
public E get(int location) {
return delegate.get(location)
}
// ...
}
我们有一个自己的 lists 实现,通过构造函数将一个 list 存储起来,存在内部的成员变量里,然后在调用相关方法的时候再委托给这个内部变量。下面是在 Kotlin 里的实现:
class MyList(list: List) : List by list
用 by 关键字,我们实现了一个存储 E 类型的 list,在调用 List 相关的方法时,会自动委托到 list 上。
译者注:参考 Kotlin 官方文档了解更多。
声明点变型(Declaration-Site Variance) (39:03)
这个可能是一个比较容易让人迷惑的主题。首先,我们用一个协变数组来开始我们的例子,下面的代码能够很好的编译:
String[] strings = { "hello", "world" };
Object[] objects = strings;
string 数组可以正常的赋值给一个 object 数组。但是下面的不行:
List strings = Arrays.asList("hello", "world");
List
项目效果展示apk : http://imtt.dd.qq.com/16891/670C11ECFEA442BE99BACA612A7EA798.apk?fsname=com.ycjt.ycnyzx_1.0.1_1.apk&csr=1bbd
或 : http://android.myapp.com/myapp/detail.htm?apkName=com.ycjt.ycnyzx
安卓开发交流群 : 595856941