基本用法
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<String>表示字符串类型的数组 ${args[0]} 表示数组中第0个元素的值
1. //字符串模板的用法 t?
2. fun stringTempl(args: Array<String>) {
3. if(args.size > 0)
4. println("args[0] = ${args[0]}")
5. }
6.
7. //main方法是整个程序的入口
8. fun main(args: Array<String>){
9. var arr = arrayOf("hello", "world")
10. stringTempl(arr)
11. }
上面的代码执行后,在控制台打印如下内容:
我们来检查我们是否给 main 函数传递了参数。先来判断这个字符串数组是不是空,如果不为空,我们把第一个字符串分配给 name 变量。Kotlin 里有个 val 类型的声明方法,类似 Java 里的 final,也就是常量。
fun main(args: Array<String>) {
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<String>) {.
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<String>) {
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<Int>) {
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<String>) {
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<T, R> {
R call(T t);
}
public static <T> List<T> filter(Collection<T> items, Function<T, Boolean> f) {
final List<T> filtered = new ArrayList<T>();
for (T item : items) if (f.call(item)) filtered.add(item);
return filtered;
}
filter(numbers, new Function<Integer, Boolean>() {
@Override
public Boolean call(Integer value) {
return value % 2 == 0;
}
});
我们首先要声明一个函数接口,接受参数类型为 T,返回类型为 R。我们用接口中的方法遍历操作了目标集合,创建了一个新的列表,把符合条件的过滤了出来。
fun <T> filter(items: Collection<T>, f: (T) -> Boolean): List<T> {
val filtered = arrayListOf<T>()
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 <T> filter(items: Collection<T>, f: (T) -> Boolean): List<T> {
val filtered = arrayListOf<T>()
for (item in items) if (f(item)) filtered.add(item)
return filtered
}
filter(numbers) { it % 2 == 0 }
由 inline 关键字在编译后会变成如下这样:
val filtered = arrayListOf<T>()
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 <T : Any> 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 <reified T : Any> 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<Customer, Order> customerMapper = // ...
final Function<Order, Boolean> orderFilter = // ...
final Function<Order, Float> orderSorter = // ...
final List<Order> 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<E> implements List<E> {
private List<E> delegate;
public MyList(List<E> delegate) {
this.delegate = delegate;
}
// ...
public E get(int location) {
return delegate.get(location)
}
// ...
}
我们有一个自己的 lists 实现,通过构造函数将一个 list 存储起来,存在内部的成员变量里,然后在调用相关方法的时候再委托给这个内部变量。下面是在 Kotlin 里的实现:
class MyList<E>(list: List<E>) : List<E> by list
用 by 关键字,我们实现了一个存储 E 类型的 list,在调用 List 相关的方法时,会自动委托到 list 上。
译者注:参考 Kotlin 官方文档了解更多。
声明点变型(Declaration-Site Variance) (39:03)
这个可能是一个比较容易让人迷惑的主题。首先,我们用一个协变数组来开始我们的例子,下面的代码能够很好的编译:
String[] strings = { "hello", "world" };
Object[] objects = strings;
string 数组可以正常的赋值给一个 object 数组。但是下面的不行:
List<String> strings = Arrays.asList("hello", "world");
List<Object> objects = strings;
你不能分配一个 string 类型的 list 给一个 object 类型的 list。因为 list 之间是没有继承关系的。如果你编译这个代码,会得到一个类型不兼容的错误。想要修复这个错误,我们得用到 Java 中的点变型(use-site variance)去声明,所谓的点变型就是在声明 list 可接受类型的时候,用extends 关键字给出参数类型的可接受类型范围,比如类似如下的例子:
译者注:点变型只是一个名字,不要太在意为什么叫这个,简单理解就是类似通配符原理,具体可以查看这个维基页面。
public interface List<E> extends Collection<E> {
public boolean addAll(Collection<? extends E> collection);
public E get(int location);
}
addAll 方法可以接受一个参数,参数类型为所有继承自 E 的类型,这不是一个具体类型,而是一个类型范围。每次调用 get 方法时,依然返回类型 E。在 Kotlin 中,你可以用 out 关键字来实现类似的功能:
public interface List<out E> : Collection<E> {
public fun get(index: Int): E
}
public interface MutableList<E> : List<E>, MutableCollection<E> {
override fun addAll(c: Collection<E>): Boolean
}
上面的一系列被称为声明点变型,即在声明可接受参数的时候,就声明为它是可变的。比如上面例子:我们声明参数是可以允许所有继承自 E 类型的,返回类型也为 E 的。
现在,我们有了可变和不可变类型的列表。可变性(variance) 其实很简单,就是取决于我们在声明的时候是动作。
译者注:其实不论声明点变型(Declaration-Site Variance) 还是 点变型(Use-site variance) 都是为了实现泛型的类型声明,标注泛型类型可支持的范围,厘清泛型类型上下继承边界。参考Generic Types。
操作符重载 (41:26)
enum class Coin(val cents: Int) {
PENNY(1),
NICKEL(5),
DIME(10),
QUARTER(25),
}
class Purse(var amount: Float) {
fun plusAssign(coin: Coin): Unit {
amount += (coin.cents / 100f)
}
}
var purse = Purse(1.50f)
purse += Coin.QUARTER // 1.75
purse += Coin.DIME // 1.85
purse += Coin.PENNY // 1.86
上面的代码中,我们创建了一个硬币枚举,每个硬币枚举都代表一个特定数额的硬币。我们有一个 Purse(钱包) 类, 它拥有一个 amount 成员变量,代表钱包里现在有多少钱。我们创建了一个叫做 plusAssign 的函数,plusAssign 是一个保留关键字。这个函数会重载 += 操作符,也就是说当你在调用 += 符号的时候,就会调用这个函数。
随后,创建一个 purse 实例,可以直接用 += 操作来实现给钱包里放钱进去。
============================================================
创建bean类
class Person{
var name: String = ""
var age: Int = 0
var college: String? = null
}
声明变量必须使用关键字var,而如果要创建一个只读/只赋值一次的变量,则需要使用val代替它
在上述代码中,变量name和age不可为空,而?表明变量college可以为空
创建实例
var jake = Person()
注意,Kotlin没有关键字new。 实例创建完成后,就可以像在Java中一样为变量赋值了:
1.直接赋值
jake.name = "Jake Hill"
jake.age = 24
jake.college = "Stephen's College"
2.通过构造函数赋值
class Person(var name: String, var age: Int, var college: String?) {
}
由于构造函数中没有其它操作,所以花括号也可以省略
class Person(var name: String, var age: Int, var college: String?)
var jake = Person("Jake Hill", 24, "Stephen's College")
上述代码中的构造函数是类头的一部分,称为主构造函数。在Kotlin中,还可以使用constructor关键字创建辅助构造函数,例如,下面的代码增加了一个辅助构造函数初始化变量email:
class Person(var name: String, var age: Int, var college: String?) {
var email: String = ""
constructor(name:String, age:Int, college: String?, email: String) : this(name, age, college) {
this.email = email
}
}
Kotlin允许创建派生类,但要遵循如下规则:
● 必须使用:代替Java中的extends关键字
● 基类头必须有open注解
● 基类必须有一个带参数的构造函数,派生类要在它自己的头中初始化那些参数
比如下面的代码创建了一个名为Empoyee的派生类:
open class Person(var name: String, var age: Int, var college: String?) {
...
}
class Employee(name: String, age: Int, college: String?, var company: String) : Person(name, age, college) {
}
======================================================
函数与扩展
有派生就有重载。与类的派生一样,允许重载的方法要有open注解,而在派生类中重载时要使用override注解。例如,下面是在Employee类中重载Person类的isEligibleToVote方法的代码:
override fun isEligibleToVote(): Boolean {
return true
}
除了改变类的已有行为,Kotlin还允许开发者在不修改类的原始定义的情况下实现对类的扩展,如下面的代码为Person类增加了一个名为isTeenager的扩展:
fun Person.isTeenager(): Boolean {
return age in 13..19
}
上面提到的函数都与Java中的函数类似,但Kotlin还支持其它类型的函数。如果一个函数返回单个表达式的值,那么可以使用=来定义函数。下面是一个创建单表达式函数的例子:
fun isOctogenarian(): Boolean = age in 80 .. 89
Kotlin还支持高阶函数和Lambda表达式。例如,lambda表达式{x,y->x+y}可以像下面这样给一个变量赋值:
val sumLambda: (Int, Int) -> Int = {x,y -> x+y}
而下面的高阶函数将上述表达式作为一个参数,并将表达式的计算结果翻倍:
fun doubleTheResult(x:Int, y:Int, f:(Int, Int)->Int): Int {
return f(x,y) * 2
}
该函数可以使用下面的其中一种方式调用:
val result1 = doubleTheResult(3, 4, sumLambda)
或
val result2 = doubleTheResult(3, 4, {x,y -> x+y})
================================================================
范围表达式
在Kotlin中,范围表达式用的比较多。范围创建只需要..操作符,例如:
val r1 = 1..5
//该范围包含数值1,2,3,4,5
如果创建一个降序范围,则需要使用downTo函数,例如:
val r2 = 5 downTo 1
//该范围包含数值5,4,3,2,1
如果步长不是1,则需要使用step函数,例如:
val r3 = 5 downTo 1 step 2
//该范围包含数值5,3,1
===============================================================
条件结构
在Kotlin中,if是一个表达式,根据条件是否满足返回不同的值,例如,下面的代码将isEligibleToVote设置为“Yes”
var age = 20
val isEligibleToVote = if(age > 18) "Yes" else "No"
when表达式相当于Java的switch,但功能更强大,例如,下面的代码将typeOfPerson设置为“Teenager”:
val age = 17
val typeOfPerson = when(age){
0 -> "New born"
in 1..12 -> "Child"
in 13..19 -> "Teenager"
else -> "Adult"
}
==============================================================
循环结构
Kotlin使用for..in遍历数组、集合及其它提供了迭代器的数据结构,语法同Java几乎完全相同,只是用in操作符取代了:操作符,例如,下面的代码将遍历一个String对象数组:
val names = arrayOf("Jake", "Jill", "Ashley", "Bill")
for (name in names) {
println(name)
}
while和do..while循环的语法与Java完全相同。
==========================================================================
枚举 enum 类。
类语法 (5:19)
我们来看看类。类的定义要通过 class 关键字,跟 Java 里的一样,关键字后是类名。Kotlin 有一个主构造函数,我们可以直接将构造函数参数列表写在类的声明处,还可以直接用 var 或者 val关键字将参数声明为成员变量(又称:类属性),如下:
class Person(var name: String)
继续之前的例子,有了主构造函数以后,我们就不再需要成员变量赋值语句了。在 Kotlin 里创建实例的时候,不必使用 new 关键字。你只需要指明创建的类型名就可以创建实例了。
class Person(var name: String)
fun main(args: Array<String>) {
val person = Person("Michael")
println("Hello, $name!")
}
很容易发现,字符串插值实际上是错误的,因为 name 指向的是一个不存在的变量了。我们可以用刚才提到的 字符串插值表达式 ,即用 $ 符号和大括号包裹想要插入的变量,来修复这个问题:
class Person(var name: String)
fun main(args: Array<String>) {
val person = Person("Michael")
println("Hello, ${person.name}!")
}
下面是 enum 类。枚举跟 Java 里的枚举很像。定义一个枚举的方法如下:
enum class Language(val greeting: String) {
EN("Hello"), ES("Hola"), FR("Bonjour")
}
我们来给 Person 类增加一个叫 lang 的属性,代表一个人的所说的语言。
class Person(var name: String, var lang: Language = Language.EN)
Kotlin 支持参数默认值,如上:language 的默认值就是 Language.EN,这样就可以在创建实例的时候忽略这个参数,除非你要改变 language 的属性值。我们来把这个例子变得更面向对象一些,给 person 增加一个打招呼的方法,简单地输出特定语言打招呼的方法还有人名:
enum class Language(val greeting: String) {
EN("Hello"), ES("Hola"), FR("Bonjour")
}
class Person(var name: String, var lang: Language = Language.EN) {
fun greet() = println("${lang.greeting}, $name!")
}
fun main(args: Array<String>) {
val person = Person("Michael")
person.greet()
}
现在在 main 函数里调用 person.greet() 方法,看看是不是很酷?!
集合和迭代 (11:32)
val people = listOf(
Person("Michael"),
Person("Miguel", Language.SP),
Person("Michelle", Language.FR)
)
我们可以用标准库函数 listOf 方法创建一个 person 列表。遍历这些 person 可以用 for-in 关键字:
for (person in people) {
person.greet()
}
随后,我们可以在每次遍历的时候执行 person.greet() 方法,甚至可以更简单,直接调用 people 集合的扩展方法 forEach,传入一个 lambda 表达式,在表达式里用 it 代表每次遍历到的person 对象,然后调用它们的 greet 方法。
people.forEach { it.greet() }
我们来创建两个新的类,每个都传入一个默认的语系。我们可以不再像刚才那样重复声明,可以直接用继承的方法来实现。下面是一个扩展版本的 Hello World。展示了很多 Kotlin 的特性:
enum class Language(val greeting: String) {
EN("Hello"), ES("Hola"), FR("Bonjour")
}
open class Person(var name: String, var lang: Language = Language.EN) {
fun greet() = println("${lang.greeting}, $name!")
}
class Hispanophone(name: String) : Person(name, Language.ES)
class Francophone(name: String) : Person(name, Language.FR)
fun main(args: Array<String>) {
listOf(
Person("Michael"),
Hispanophone("Miguel"),
Francophone("Michelle")
).forEach { it.greet() }
}
===============================================================================
项目效果展示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