kotlin是什么?
Kotlin是JVM和Android的实用编程语言,专注于互操作性,安全性,清晰度和工具支持。作为通用语言,Kotlin 可以在Java工作的地方工作如:
服务器端应用程序,移动应用程序(Android),桌面应用程序。它适用于所有主要的工具和服务,如 IntelliJ IDEA,Android Studio和Eclipse;
Maven,Gradle和Ant;Spring Boot;GitHub,Slack甚至Minecraft。
kotlin代码规范:
a.命名与Java相似:使用驼峰命名,类型名称首字母大写,方法和属性首字母小写
b.冒号的含义:冒号区分类型和父类型中要有空格,在实例和类型之间是没有空格。
c.unit 表示无返回值类型
kotlin语法一:
a.定义函数
定义一个函数接受两个 int 型参数,返回值为 int :
fun sum(a: Int , b: Int) : Int{
return
a + b
}
fun main(args: Array<String>) {
print(
"sum of 3 and 5 is "
)
println(sum(
3
,
5
))
}
|
复制代码
注:var 是可变的,val 是只读
b.定义局部变量
声明常量:
fun main(args: Array<String>) {
val a: Int =
1
// 立即初始化
val b =
2
// 推导出Int型
val c: Int
// 当没有初始化值时必须声明类型
c =
3
// 赋值
println(
"a = $a, b = $b, c = $c"
)
}
|
变量:
fun main(args: Array<String>) {
var x =
5
// 推导出Int类型
x +=
1
println(
"x = $x"
)
}
|
c.for循环:
fun main(args: Array<String>) {
val items = listOf(
"apple"
,
"banana"
,
"kiwi"
)
for
(item in items) {
println(item)
}
}
|
在kotlin中for循环可以支持的类型有集合、数组、字符串;复制代码
1.集合(list,set,map)
var str = listOf(
1
,
2
,
3
,
4
)
for
(a in str){
println(
"$a"
)
}
|
打印:
1
2
3
4
var str = mapOf(
"a"
to
1
,
"b"
to
2
,
"c"
to
3
,
"d"
to
4
)
for
(a in str){
println(
"map集合:$a"
)
}
|
打印:
map集合:a=1
map集合:b=2
map集合:c=3
map集合:d=4
2.数组
var str = arrayOf(
1
,
2
,
3
,
4
)
for
(a in str){
println(
"数组:$a"
)
}
|
打印:
数组:1
数组:2
数组:3
数组:4
3.字符串类型
var str =
"asdfghhj"
for
(a in str){
println(
"字符串:$a"
)
}
|
打印:
字符串:a
字符串:s
字符串:d
字符串:f
字符串:g
字符串:h
字符串:h
字符串:j
与Java区别:
List<Object> list =
new
ArrayList<>();
list.add(
1
);
list.add(
2
);
list.add(
3
);
list.add(
"a"
);
list.add(
true
);
或者:
//创建集合
Collection coll =
new
ArrayList();
((ArrayList) coll).add(
"a"
);
((ArrayList) coll).add(
1
);
((ArrayList) coll).add(
false
);
for
(Object s:list){
System.out.println(s);
}
|
Java创建集合时候,类型已经确定,想放入多种类型的数据,可以将泛型声明为Object,但是kotlin可以不确定,可以一个集合放多种类型的数据;
var str = listOf(
'a'
,
1
,
true
)
for
(a in str){
println(
"$a"
)
}
|
d.使用 while 循环
fun main(args: Array<String>) {
val items = listOf(
"apple"
,
"banana"
,
"kiwi"
)
var index =
0
while
(index < items.size) {
println(
"item at $index is ${items[index]}"
)
index++
}
}
|
复制代码
e.使用when表达式
fun describe(obj: Any): String =
when (obj) {
1
->
"One"
"Hello"
->
"Greeting"
is Long ->
"Long"
!is String ->
"Not a string"
else
->
"Unknown"
}
fun main(args: Array<String>) {
println(describe(
1
))
println(describe(
"Hello"
))
println(describe(1000L))
println(describe(
2
))
println(describe(
"other"
))
}
|
f.字符串的表示
fun main(args: Array<String>) {
var a =
1
// 使用变量名作为模板:
val s1 =
"a is $a"
a =
2
// 使用表达式作为模板:
val s2 =
"${s1.replace("
is
", "
was
")}, but now is $a"
println(s2)
}
|
注意:打印println时需要加"$"
g.集合的遍历
fun main(args: Array<String>) {
val items = listOf(
"apple"
,
"banana"
,
"kiwi"
)
for
(item in items) {
println(item)
}
}
|
注:使用in操作符检查集合是否包含某个对象
kotlin语法二:
a.基本数据类型-数值:
Double
Float
Long
Int
Short
Byte
b.数据转换
kotlin不支持隐式转换数值,如;
val b: Byte =
1
// OK, literals are checked statically val i: Int = b.toInt
|
//这里必须要用toInt转换成int数据类型复制代码
每个数值类型都支持下面的转换:
toByte(): Byte
toShort(): Short
toInt(): Int
toLong(): Long
toFloat(): Float
toDouble(): Double
toChar(): Char
|
c.运算符
至于位运算,Kotlin 并没有提供特殊的操作符,只是提供了可以叫中缀形式的方法,比如:
val x = (1 shl 2) and 0x000FF000
下面是全部的位运算操作符(只可以用在 Int
和 Long
类型):
shl(bits)
– 带符号左移 (相当于 Java’s<<
)shr(bits)
– 带符号右移 (相当于 Java’s>>
)ushr(bits)
– 无符号右移 (相当于 Java’s>>>
)and(bits)
– 按位与or(bits)
– 按位或xor(bits)
– 按位异或inv(bits)
– 按位翻转
d.字符
字符类型用Char表示,不能直接当做数值来使用
字符是由单引号包裹的'1',特殊的字符通过反斜杠\转义,下面的字符序列支持转义:\t,\b,\n,\r,',",\和$。编码任何其他字符,使用 Unicode 转义语法:\uFF00。
我们可以将字符显示的转义为Int数字:
fun decimalDigitValue(c: Char): Int {
if
(c !in
'0'
..
'9'
)
throw
IllegalArgumentException(
"Out of range"
)
return
c.toInt() -
'0'
.toInt()
//显示转换为数值类型
}
|
e.布尔值复制代码
这个同Java只有true和false
f.Array
Arrays在 Kotlin 中由 Array
类表示,有 get
和 set
方法(通过运算符重载可以由[]调用),以及 size
方法,以及一些常用的函数:
class
Array<T>
private
() {
fun size(): Int
fun get(index: Int): T
fun set(Index: Int, value: T): Unit
fun iterator(): Iterator<T>
//...
}
|
我们可以给库函数 arrayOf()
传递每一项的值来创建Array,arrayOf(1, 2, 3)
创建了一个[1, 2, 3] 这样的数组。也可以使用库函数 arrayOfNulls()
创建一个指定大小的空Array。
g.if表达式
if 分支可以作为块,最后一个表达是是该块的值:
val max =
if
(a > b){
print(
"Choose a"
)
a
}
else
{
print(
"Choose b"
)
b
}
|
如果 if 表达式只有一个分支,或者分支的结果是 Unit
, 它的值就是 Unit
。
h.返回与跳转
break和continue
kotlin语法三:
a.类
在 Kotlin 中类用 class 声明:复制代码
class
Invoice { }
|
类的声明包含类名,类头(指定类型参数,主构造函数等等),以及类主体,用大括号包裹。类头和类体是可选的;如果没有类体可以省略大括号。复制代码
复制代码
class
Empty
|
b.构造函数
在 Kotlin 中类可以有一个主构造函数以及多个二级构造函数。主构造函数是类头的一部分:跟在类名后面(可以有可选的类型参数)。复制代码
class
Person constructor(firstName: String) {
}
|
如果主构造函数没有注解或可见性说明,则 constructor
关键字是可以省略:
class
Person(firstName: String){
}
|
主构造函数不能包含任意代码。初始化代码可以放在以 init 做前缀的初始化块内复制代码
class
Customer(name: String) {
init {
logger,info(
"Customer initialized with value ${name}"
)
}
}
|
事实上,声明属性并在主构造函数中初始化,在 Kotlin 中有更简单的语法:
class
Person(val firstName: String, val lastName: String, var age: Int) {
}
|
就像普通的属性,在主构造函数中的属性可以是可变的(var
)或只读的(val
)。
如果构造函数有注解或可见性声明,则 constructor
关键字是不可少的,并且可见性应该在前:
class
Customer
public
@inject
constructor (name: String) {...}
|
c.二级构造函数
类也可以有二级构造函数,需要加前缀 constructor
:
class
Person {
constructor(parent: Person) {
parent.children.add(
this
)
}
}
|
如果类有主构造函数,每个二级构造函数都要,或直接或间接通过另一个二级构造函数代理主构造函数。在同一个类中代理另一个构造函数使用 this 关键字:复制代码
class
Person(val name: String) {
constructor (name: String, paret: Person) :
this
(name) {
parent.children.add(
this
)
}
}
|
如果一个非抽象类没有声明构造函数(主构造函数或二级构造函数),它会产生一个没有参数的构造函数。该构造函数的可见性是 public 。如果你不想你的类有公共的构造函数,你就得声明一个拥有非默认可见性的空主构造函数:
class
DontCreateMe
private
constructor () {
}
|
注意:在 JVM 虚拟机中,如果主构造函数的所有参数都有默认值,编译器会生成一个附加的无参的构造函数,这个构造函数会直接使用默认值。这使得 Kotlin 可以更简单的使用像 Jackson 或者 JPA 这样使用无参构造函数来创建类实例的库。
复制代码
class
Customer(val customerName: String =
""
)
|
d.继承
Kotlin 中所有的类都有共同的父类 Any
,它是一个没有父类声明的类的默认父类:
class Example // 隐式继承于 Any复制代码
Any
不是 java.lang.Object
;事实上它除了 equals()
,hashCode()
以及toString()
外没有任何成员了。
声明一个明确的父类,需要在类头后加冒号再加父类:
如果类没有主构造函数,则必须在每一个构造函数中用 super
关键字初始化基类,或者在代理另一个构造函数做这件事。注意在这种情形中不同的二级构造函数可以调用基类不同的构造方法
class
MyView : View {
constructor(ctx: Context) :
super
(ctx) {
}
constructor(ctx: Context, attrs: AttributeSet) :
super
(ctx,attrs) {
}
}
|
e.重写方法
kotlin需要把可以复写的成员都明确注解出来,并且重写他们:
open
class
Base {
open fun v() {}
fun nv() {}
}
class
Derived() : Base() {
override fun v() {}
}
|
对于 Derived.v()
来说override
注解是必须的。如果没有加的话,编译器会提示。如果没有open
注解,像 Base.nv()
,在子类中声明一个同样的函数是不合法的,要么加override
要么不要复写。在 final 类(就是没有open注解的类)中,open
类型的成员是不允许的。
标记为override
的成员是open的,它可以在子类中被复写。如果你不想被重写就要加 final:
open
class
AnotherDerived() : Base() {
final
override fun v() {}
}
|
f.重写的规则
在 kotlin 中,实现继承通常遵循如下规则:如果一个类从它的直接父类继承了同一个成员的多个实现,那么它必须复写这个成员并且提供自己的实现(或许只是直接用了继承来的实现)。为表示使用父类中提供的方法我们用 super<Base>
表示:
open
class
A {
open fun f() {
print(
"A"
)
}
fun a() {
print(
"a"
)
}
}
interface
B {
fun f() {
print(
"B"
)
}
// 接口的成员变量默认是 open 的 fun b() { print("b") } } class C() : A() , B { // 编译器会要求复写f() override fun f() { super<A>.f() // 调用 A.f() super<B>.f() // 调用 B.f() } }
}
|
可以同时从 A 和 B 中继承方法,而且 C 继承 a() 或 b() 的实现没有任何问题,因为它们都只有一个实现。但是 f() 有俩个实现,因此我们在 C 中必须复写 f() 并且提供自己的实现来消除歧义。复制代码
g.密封类
申明密封类需要在class前面加上sealed修饰符复制代码
h.数据类
数据类
有着和Kotlin
其他类不一样的特性。除了含有其他类的一些特性外,还有着其独特的特点。并且也是数据类
必须满足的条件:
- 主构造函数需要至少有一个参数
- 主构造函数的所有参数需要标记为 val 或 var;
- 数据类不能是抽象、开放、密封或者内部的;
- 数据类是可以实现接口的,如(序列化接口),同时也是可以继承其他类的,如继承自一个密封类。
比Java简洁;如:复制代码
kotlin:
data
class
User(val name: String, val pwd: String)
|
java:复制代码
class
User {
var name: String? =
null
var pwd: String? =
null
constructor() {}
constructor(name: String, pwd: String) {
this
.name = name
this
.pwd = pwd
}
override fun toString(): String {
return
"User{"
+
"name='"
+ name + '\
''
.toString() +
", pwd='"
+ pwd + '\
''
.toString() +
'}'
.toString()
}
}
|
复制代码
注意:修改数据属性值也与Java有很大区别;kotlin更改属性值需要用copy()方法,Java则是使用set()方法。复制代码
val mUser = User(
"kotlin"
,
"123456"
)
println(mUser)
val mNewUser = mUser.copy(name =
"new Kotlin"
)
println(mNewUser)
|
输出结果为:
User(name=kotlin, pwd=
123456
)
User(name=
new
Kotlin, pwd=
123456
)
|
Java代码为:复制代码
User mUser =
new
User(
"Java"
,
"123456"
);
System.out.println(mUser);
mUser.setName(
"new Java"
);
System.out.println(mUser);
|
输出结果为:
User{name=
'Java'
, pwd=
'123456'
}
User{name=
'new Java'
, pwd=
'123456'
}
|
kotlin语法四:
a.泛型
java 类型系统最棘手的一部分就是通配符类型。但 kotlin 没有,代替它的是两种其它的东西:声明变化和类型投影。Kotlin泛型并没有提供通配符,取而代之的是out和in关键字。
用out声明的泛型占位符只能在获取泛型类型值得地方,如函数的返回值。用in声明的泛型占位符只能在设置泛型类型值的地方,如函数的参数
abstract
class
Source<out T> {
abstract
fun func(): T
}
abstract
class
Comparable<in T> {
abstract
fun func(t: T)
}
|
类型投影:如果将泛型类型T声明为out,就可以将其子类化(List < String > 是List < Object> 的子类型),这是非常方便的。如果我们的类能够仅仅只返回T类型的值,那么的确可以将其子类化。
现在有一个Array类如下:
class
Array<T>(val size: Int) {
fun get(index: Int): T {
}
fun set(index: Int, t: T) {
}
}
|
此类中的T既是get方法的返回值,又是set方法的参数,也就是说Array类既是T的生产者,也是T的消费者,这样的类就无法进行子类化。
fun copy(from: Array<Any>, to: Array<Any>) {
assert
(from.size == to.size)
for
(i in from.indices) {
to[i] = from[i]
}
}
|
这个copy方法,就是将一个Array复制到另一个Array中,现在尝试使用一下:
val ints: Array<Int> = arrayOf(
1
,
2
,
3
)
val any: Array<Any> = Array(
3
)
copy(ints, any)
// 编译错误,因为Array<Int> 不是Array<Any>的子类型
|
Array< T > 对于类型参数T是不可变的,因此Array< Int> 和Array< Any>他们没有任何关系,为什么呢?因为copy可能会进行一些不安全的操作,也就是说,这个函数可能会试图向from中写入数据,这样可能会抛类型转换异常。
可以这样:
fun copy(from: Array<out Any>, to: Array<Any>) {
...
}
|
将from的泛型使用out修饰。
这种声明在Kotlin中称为类型投射:
from不是一个单纯的数组,而是一个被限制(投射)的数组,我们只能对这个数组调用那些返回值为类型参数T的函数,在这个例子中,我们只能调用get方法,这就是我们事先使用处的类型变异的方案。
in关键字也是同理
kotlin语法五:
a.嵌套类、内部类、抽象类
class
Outer {
private
val bar: Int =
1
class
Nested {
fun foo() =
2
}
}
val demo = Outer.Nested().foo()
//==2
|
内部类:复制代码
类可以标记为 inner 这样就可以访问外部类的成员。内部类拥有外部类的一个对象引用:
class
Outer {
private
val bar: Int =
1
inner
class
Inner {
fun foo() = bar
}
}
val demo = Outer().Inner().foo()
//==1
|
匿名内部类:复制代码
匿名内部类的实例是通过 对象表达式 创建的:
window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
// ...
}
override fun mouseEntered(e: MouseEvent) {
// ...
}
})
|
抽象类:复制代码
抽象类,可以理解为类定义了一个模板。所有的子类都是根据这个模板是填充自己的代码。声明一个抽象(类或函数)的关键字为:abstract其中值得注意的是:抽象可以分为抽象类、抽象函数、抽象属性。而一个抽象类和普通类的区别在于抽象类除了可以有其自己的属性、构造函数、方法等组成部分,还包含了抽象函数以及抽象属性。复制代码
abstract
class
Lanauage {
val TAG =
this
.javaClass.simpleName
// 自身的属性
// 自身的函数
fun test(): Unit {
// exp
}
abstract
var name: String
// 抽象属性
abstract
fun init()
// 抽象方法
}
/**
* 抽象类Lanauage的实现类TestAbstarctA
*/
class
TestAbstarctA : Lanauage() {
override var name: String
get() =
"Kotlin"
set(value) {}
override fun init() {
println(
"我是$name"
)
}
}
/**
* 抽象类Lanauage的实现类TestAbstarctB
*/
class
TestAbstarctB : Lanauage() {
override var name: String
get() =
"Java"
set(value) {}
override fun init() {
println(
"我是$name"
)
}
}
fun main(args: Array<String>) {
// val lanauage = Lanauage() 是错误的,因为抽象类不能直接被实例化
val mTestAbstarctA = TestAbstarctA()
val mTestAbstarctB = TestAbstarctB()
println(mTestAbstarctA.name)
mTestAbstarctA.init()
println(mTestAbstarctB.name)
mTestAbstarctB.init()
}
|
输出结果为:
Kotlin
我是Kotlin
Java
我是Java复制代码
注意:抽象类本身具有普通类特性,以及组成部分。不过值得注意的是,抽象类不能直接被实例化复制代码
其抽象了类的子类必须全部重写带abstract修饰的属性和方法。复制代码
抽象成员只有定义,没有实现。都有abstract修饰符修饰。复制代码
抽象类是为其子类定义了一个模板。不同是类实现不同的功能。复制代码
b.枚举类
1.声明方式及枚举常量enum
关键字在类头中的class
关键字前面
2.枚举常量:枚举类中的每一个枚举常量都是一个对象,并且他们之间用逗号分隔。
/** * 例:关于一个网络请求结果的枚举类 */
enum
class
State {
/* NORMAL : 正常
NO_DATA : 数据为空
NO_INTERNET : 网络未连接
ERROR : 错误
OTHER : 其他 */
NORMAL,
NO_DATA, NO_INTERNET, ERROR, OTHER
}
|
3.访问枚举常量
不需要实例化枚举类就可以访问枚举常量,
使用方式为:
枚举类名.枚举常量.属性;State.NORMAL.name4.枚举常量的匿名类复制代码
要实现枚举常量的匿名类,则必须提供一个抽象方法(必须重写的方法)。且该方法定义在枚举类内部。而且必须在枚举变量的后面。
枚举变量之间使用逗号(
,
)分割开。但是最后一个枚举变量必须使用分号结束。不然定义不了抽象方法。fun main(args: Array<String>) {
ConsoleColor.BLACK.print()
}
enum
class
ConsoleColor(var argb: Int) {
RED(
0xFF0000
) {
override fun print() {
println(
"我是枚举常量 RED "
)
}
},
WHITE(
0xFFFFFF
) {
override fun print() {
println(
"我是枚举常量 WHITE "
)
}
},
BLACK(
0x000000
) {
override fun print() {
println(
"我是枚举常量 BLACK "
)
}
},
GREEN(
0x00FF00
) {
override fun print() {
println(
"我是枚举常量 GREEN "
)
}
};
abstract
fun print() }
5.枚举类的使用
每个枚举常量都包含两个属性:
name(枚举常量名)
和ordinal(枚举常量位置)
提供了
values()
和valueOf()
方法来检测指定的名称与枚举类中定义的任何枚举常量是否匹配。Kotlin 1.1
起,可以使用 enumValues<T>()
和 enumValueOf<T>()
函数以泛型的方式访问枚举类中的常量c.接口
关键字:
interface
用法
关键字:冒号(:
),这一点是和Java
不同的。Java
中使用接口使用的是implements
关键字
在Kotlin
中冒号(:
)使用的地方很多:
用于变量的定义
用于继承
用于接口
方法的返回类型声明
使用格式:
class
类名 : 接口名{
// 重写的接口函数、属性等 ... }
|
例子:
fun main(args: Array<String>) {
// 类的初始化 var demo = Demo1() demo.fun1()
}
/** * 我定义的接口 */
interface
Demo1Interface {
// 定义的方法 fun fun1() }
/** * 接口的实现类 */
class
Demo1 : Demo1Interface {
override fun fun1() {
println(
"我是接口中的fun1方法"
)
}
}
}
|
d.实现多个接口,重写不同接口的相同方法名
//接口类
fun main(args: Array<String>) {
// 类的初始化
val demo = Demo4()
demo.fun1()
demo.fun2()
}
interface
Demo4InterfaceOne{
fun fun1(){
println(
"我是Demo4InterfaceOne中的fun1()"
)
}
fun fun2(){
println(
"我是Demo4InterfaceOne中的fun2()"
)
}
}
interface
Demo4InterfaceTwo{
fun fun1(){
println(
"我是Demo4InterfaceTwo中的fun1()"
)
}
fun fun2(){
println(
"我是Demo4InterfaceTwo中的fun2()"
)
}
}
class
Demo4 : Demo4InterfaceOne,Demo4InterfaceTwo{
override fun fun1() {
super
<Demo4InterfaceOne>.fun1()
super
<Demo4InterfaceTwo>.fun1()
}
//可以通过super<接口名>.方法名来区分
override fun fun2() {
super
<Demo4InterfaceOne>.fun2()
super
<Demo4InterfaceTwo>.fun2()
}
}
|
说明:
Demo4
实现了Demo4InterfaceOne
和Demo4InterfaceTwo
两个接口,而两个接口中都存在两个相同方法名的方法。因此编译器不知道应该选哪个,故而我们用
super<接口名>.方法名
来区分
e.函数
在 kotlin 中用关键字 fun
声明函数:
fun
double
(x: Int): Int {
}
|
使用和Java差不多,创建对象调用方法,如
Sample().foo() // 创建Sample类的实例,调用foo方法复制代码
1.中辍函数
infix是中辍函数的修饰符
2.成员函数
成员函数是定义在一个类或对象里边的
class
Sample() {
fun foo() {
print(
"Foo"
)
}
}
|
3.泛型函数函数可以有泛型参数,样式是在函数后跟上尖括号。复制代码
fun sigletonArray<T>(item: T): Array<T> {
return
Array<T>(
1
, { item })
}
|
4.高阶函数
高阶函数就是以另外一个函数作为参数或者返回值的函数,在Kotlin中,
函数可以用lambda或者函数引用来表示。因此,任何以lambda或者函数引用作为参数的函数,
或者返回值为lambda或函数引用的函数,都是高阶函数。
例如,标准库中的filter函数将一个判断式作为参数:
list.filter { x >
0
}
|
声明函数类型,需要将函数参数类型放在括号中,紧接着是一个箭头和函数的返回类型。
val sum: (Int, Int) -> Int = { x, y -> x + y }
val action: () -> Unit = { println(
42
) }
|
在声明一个普通的函数时,Unit类型的返回值是可以省略的,但是一个函数类型声明总是需要一个显式地返回类型,所以这里Unit是不能省略的。
在lambda表达式{ x, y -> x + y }
中省略参数了类型,因为他们的类型已经在函数类型的变量声明部分指定了,不需要在lambda本身的定义中再重复声明。
就像其他方法一样,函数类型的返回值也可以标记为可空类型:
var canReturnNull: (Int, Int) -> Int? = {
null
}
|
也可以定义一个函数类型的可空变量,为了明确表示是变量本身可空,而不是函数类型的返回类型可空,你需要将整个函数类型的定义包含在括号内并在括号后面添加一个问号:
var funOrNull: ((Int, Int) -> Int)? =
null
|
注意:如果省略了括号,声明的将会是一个返回值可空的函数类型,而不是一个可空的函数类型的变量。
5.自定义高阶函数:例:传入两个参数,并传入一个函数来实现他们不同的逻辑复制代码
//自定义高阶函数/**/
private
fun resultByOpt(num1: Int, num2: Int, result: (Int, Int) -> Int): Int {
return
result(num1, num2)
}
private
fun testDemo() {
for
(x in
1
..
5
) {
for
(y in
1
..
5
) {
val result1 = resultByOpt(x, y) { num1, num2 ->
num1 + num2
}
println(
"x:$x ==> y:$y"
)
println(
"result1 = $result1"
)
println(
"========================================"
)
val result2 = resultByOpt(x, y) { num1, num2 ->
num1 - num2
}
println(
"x:$x ==> y:$y"
)
println(
"result2 = $result2"
)
println(
"------------------------------"
)
val result3 = resultByOpt(x, y) { num1, num2 ->
num1 * num2
}
println(
"x:$x ==> y:$y"
)
println(
"result3 = $result3"
)
println(
"...................................."
)
val result4 = resultByOpt(x, y) {
num1, num2 ->
num1 / num2
}
println(
"x:$x ==> y:$y"
)
println(
"result4 = $result4"
)
println(
">>>>>>>>>>>>>>>>>>>>>>>>>>>>"
)
}
}
}
|
6.添加字符串和函数类型的参数默认值:声明函数类型的参数的时候可以指定参数的默认值。复制代码
//函数类型的参数
fun <T> Collection<T>.joinToString(
separator: String =
","
,
prefix: String =
""
,
postfix: String =
""
,
transform: (T) -> String = { it.toString() }
//默认实现
): String {
val result = StringBuilder(prefix)
for
((index, element) in
this
.withIndex()) {
if
(index >
0
) result.append(separator)
result.append(transform(element))
//使用函数参数转换
println(
"index===>$index;element=====>$element"
)
println(
"result===>$result;transform(element)=====>${transform(element)}"
)
}
result.append(postfix)
println(
"postfix===>$postfix"
)
return
result.toString()
}
fun main(args: Array<String>) {
val letters = listOf(
"ALPHL"
,
"Beta"
)
println(
"===="
+ letters.joinToString())
println(
"------"
+ letters.joinToString { it.toLowerCase() })
println(
"......."
+ letters.joinToString(separator =
"! "
, postfix =
"! "
, transform = { it.toUpperCase() }))
}
|
这个一个泛型函数:它有一个类型参数T表示集合中的元素的类型。transform将接收这个类型的参数。
声明函数类型的默认值并不需要特殊的语法——只需要把lambda作为值放在=号后面。上面的例子展示了不同的函数调用方式:省略整个lambda(使用默认的toString做转换),在括号以外传递lambda,或者以命名参数形式传递。
f.lambda 表达式
- lambda 表达式总是被大括号括着,
- 其参数(如果有的话)在
->
之前声明(参数类型可以省略), - 函数体(如果存在的话)在
->
后面。
Lambda 表达式的完整语法形式,即函数类型的字面值如下:
val sum = { x: Int, y: Int -> x + y }
|
lambda 表达式总是被大括号括着, 完整语法形式的参数声明放在括号内,并有可选的类型标注, 函数体跟在一个 ->
符号之后。如果推断出的该 lambda 的返回类型不是 Unit
,那么该 lambda 主体中的最后一个(或可能是单个)表达式会视为返回值
我们先来看一个 Lambda 表达式的例子:
fun main(args: Array<String>) {
val lambda = { left: Int, right: Int
->
left + right
}
println(lambda(
2
,
3
))
}
|
大家可以看到我们定义了一个变量 lambda,赋值为一个 Lambda 表达式。Lambda
表达式用一对大括号括起来,后面先依次写下参数及其类型,如果没有就不写,接着写下 ->
,这表明后面的是函数体了,函数体的最后一句的表达式结果就是 Lambda 表达式的返回值,比如这里的返回值就是参数求和的结果。复制代码
注意:
() -> Int //无参,返回 Int
(Int, Int) -> String //两个整型参数,返回字符串类型
(()->Unit, Int) -> Unit //传入了一个 Lambda 表达式和一个整型,返回 Unit复制代码
筛选年龄:
主要是通过filter和map过滤变换复制代码
filter
是对一个集合中的所有元素应用一个表达式判断,然后返回一个判断结果为true
的元素的新集合。说白了就是从一个集合中筛选出符合条件的元素并返回一个新集合。
map
是对一个集合中的每个元素应用一个表达式,然后生成一个新的集合。
fun main(args: Array<String>) {
val num = listOf(
1
,
2
,
3
,
4
,
5
)
val peopleList = listOf(People(
"小白"
,
22
),
People(
"小红"
,
23
),
People(
"小明"
,
20
),
People(
"李刚"
,
15
))
//筛选大于等于3
println(num.filter { it >=
3
})
//筛选age小于18
println(peopleList.filter { it.age <
18
})
println(peopleList.filter { it. age>
18
}.map { it.name })
//map生成一个新的集合
}
//需要注意的是数据类需要加 data 关键字,否则打印出来是地址值
data
class
People (val name:String,val age:Int)
|
复制代码