Kotlin-值得关注的地方

Google对于kotlin这门语言,一直有所提及。作为开发人员,掌握kotlin是非常必要的。下面我么一起学习下kotlin的简洁之处。

1、告别枯燥的findviewByid

例如,如下布局:


    <TextView
         android:id="@+id/tv_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_parent"
        android:background="@drawable/line" />

在代码中直接设置文本内容:

tv_title.text="i am Asin²+cos²=1";

2、Lambda表达式

Lambda搭配kotlin使用,是个不错的选择,如下:

未使用lambda表达式前:

tv_title.setOnClickListener(View.OnClickListener(

   Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                .setAction("Action", null).show()
))

在使用lambda表达式之后:


     tv_title.setOnClickListener { view ->
            Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                .setAction("Action", null).show()
        }

3、函数变量

与java不同的是,kotlin中是可以将函数作为变量来传递的,如下代码:
 

  var result=fun(number:Int,number2:Int):Int{
        return number+number2
    }

调用:

 Log.d(TAG, result(1, 2).toString())

4、空安全

在java中不用强制我们处理空对象,所以稍加不注意就会出现NullPointerException空指针异常,现在kotlin在这一块做了限定,必须在编译时处理对象是否为空的情况,否则编译不通过

在对象不为空的情况下直接使用该对象。

如下代码:

  fun getText():String{
        return  "Asin²+cos²=1"
    }

调用如下:

    val text=getText();
        print(text.length)

如果在对象为空的情况下,则必须要判断对象是否为空了。

//加?表示可以返回null
fun getText() : String? {
    return null
}

在使用的时候:
 

val text=getText()
//需要加入非空判断,否则编译不通过
if(null!=text){
print(text.length)
}

或者:

//如果不想判断为空,也可以使用该方法,但是如果对象是为null的时候,则直接抛出空指针异常了,一般不建议这么使用
val text=getText()
print(text!!.length)

5、方法支持添加默认参数

在java中,我们可能为了扩展某个方法而进行多次重载

public void toast(String text) {
    toast(this, text, Toast.LENGTH_SHORT);
}

public void toast(Context context, String text) {
    toast(context, text, Toast.LENGTH_SHORT);
}

public void toast(Context context, String text, int time) {
    Toast.makeText(context, text, time).show();
}

而在kotlin中,我们无需进行重载,可以直接在方法上面直接定义参数的默认值:

fun toast(context:Context,text:String,time:Int=Toast.LENGTH_SHORT){
Toast.makeText(context,text,time).show()
}

调用:
 

toast(text = "carden")
toast(this, "carden")
toast(this, "carden", Toast.LENGTH_LONG)

6、类方法的扩展

kotlin可以在不用继承的情况下,对方法进行扩展。

fun String.handle():String{
return this+"Asin²+cos²=1"
}

//注意:handle方法在哪个类中被定义,这种扩展就只能在那个类中被使用
print("carden=".handle())

log------------------------------------
carden=Asin²+cos²=1

7、运算符重载:

在 Kotlin 中使用运算符最终也会调用对象对应的方法,我们可以通过重写这些方法使得这个对象支持运算符。

运算符调用方法
+aa.unaryPlus()
-aa.unaryMinus()
!aa.not()

运算符调用方法
a++a.inc()
a--a.dec()
运算符调用方法
a + ba.plus(b)
a - ba.minus(b)
a * ba.times(b)
a / ba.div(b)
a % ba.rem(b), a.mod(b) (deprecated)
a..ba.rangeTo(b)

运算符调用方法
a in bb.contains(a)
a !in b!b.contains(a)

运算符调用方法
a[i]a.get(i)
a[i, j]a.get(i, j)
a[i_1, ..., i_n]a.get(i_1, ..., i_n)
a[i] = ba.set(i, b)
a[i, j] = ba.set(i, j, b)
a[i_1, ..., i_n] = ba.set(i_1, ..., i_n, b)

运算符调用方法
a()a.invoke()
a(i)a.invoke(i)
a(i, j)a.invoke(i, j)
a(i_1, ..., i_n)a.invoke(i_1, ..., i_n)

运算符调用方法
a += ba.plusAssign(b)
a -= ba.minusAssign(b)
a *= ba.timesAssign(b)
a /= ba.divAssign(b)
a %= ba.remAssign(b), a.modAssign(b) (deprecated)

运算符调用方法
a == ba?.equals(b) ?: (b === null)
a != b!(a?.equals(b) ?: (b === null))

运算符调用方法
a > ba.compareTo(b) > 0
a < ba.compareTo(b) < 0
a >= ba.compareTo(b) >= 0
a <= ba.compareTo(b) <= 0

8、扩展函数

扩展函数是 Kotlin 用于简化一些代码的书写产生的,其中有 let、with、run、apply、also 五个函数

①、let函数

在函数块中可以使用it指向该对象,返回值为函数块的最后一行或者指定return表达式

一般写法:

val test:String="carden"
print(test.length)
val result=10000
print(result)

使用let后的写法:

val result="carden".let{
print(it.length)
10000
}
print(result)
log-------------------------------
输出结果
6
10000

最常用的场景就是使用let函数处理需要针对一个可null的对象统一做判空处理,又或者是需要去明确一个变量所处特定的作用域范围内可以使用

mVideoPlayer?.setVideoView(activity.course_video_view)
mVideoPlayer?.setControllerView(activity.course_video_controller_view)
mVideoPlayer?.setCurtainView(activity.course_video_curtain_view)
                         ||
                         ||
mVideoPlayer?.let{
it.setVideoView(activity.course_video_view)
 it.setControllerView(activity.course_video_controller_view)
   it.setCurtainView(activity.course_video_curtain_view)

}

②、with函数

前面的几个函数使用方式略有不同,因为它不是以扩展的形式存在的。它是将某对象作为函数的参数,在函数块内可以通过 this 指代该对象。返回值为函数块的最后一行或指定return表达式

一般写法:
定义Student类

class Student(name:String,age:Int)

fun main(){
var student=Student("carden","18")
println(student.name+student.age)
var result=10000
println(result)
}

使用with后的写法:

var result=with(Student("carden",18)){
println(name+age)
10000
}
print(result)
log---------------------------------------------
carden18
10000

适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法即可,经常用于Android中RecyclerView中onBinderViewHolder中,数据model的属性映射到UI上

override fun onBindViewHolder(holder: ViewHolder, position: Int){
    val item = getItem(position)?: return
    holder.nameView.text = "姓名:${item.name}"
    holder.ageView.text = "年龄:${item.age}"
}


-------->
override fun onBindViewHolder(holder: ViewHolder, position: Int){
    val item = getItem(position)?: return
    with(item){
        holder.nameView.text = "姓名:$name"
        holder.ageView.text = "年龄:$age"
    }
}

③、run函数

实际上可以说是let和with两个函数的结合体,run函数只接收一个lambda函数为参数,以闭包形式返回,返回值为最后一行的值或者指定的return的表达式

一般写法为:
 

var student= Studen("carden", 18)
println(student.name + "+" + student.age)
var result = 10000
println(result)

run函数写法为

var student=Student("carden",18)
var result=student.run{
println("$name+$age")
10000
}
println(result)

适用于let,with函数任何场景。因为run函数是let,with两个函数结合体,准确来说它弥补了let函数在函数体内必须使用it参数替代对象,在run函数中可以像with函数一样可以省略,直接访问实例的公有属性和方法,另一方面它弥补了with函数传入对象判空问题,在run函数中可以像let函数一样做判空处理,这里还是借助 onBindViewHolder 案例进行简化

override fun onBindViewHolder(holder: ViewHolder, position: Int){
    val item = getItem(position)?: return
    holder.nameView.text = "姓名:${item.name}"
    holder.ageView.text = "年龄:${item.age}"
}
--------------->
override fun onBindViewHolder(holder: ViewHolder, position: Int){
    val item = getItem(position)?: return
    item?.run {
        holder.nameView.text = "姓名:$name"
        holder.ageView.text = "年龄:$age"
    }
}

④、apply函数

从结构上来看apply函数和run函数很像,唯一不同点就是它们各自返回的值不一样,run函数是以闭包形式返回最后一行代码的值,而apply函数的返回的是传入对象的本身

一般写法:
 

val student= Student("carden", 18)
student.name = "cardenzhang"
student.age =20

apply写法
 

var student=Student("carden",18).apply{
name="cardenzhang"
age=20
}

整体作用功能和run函数很像,唯一不同点就是它返回的值是对象本身,而run函数是一个闭包形式返回,返回的是最后一行的值。正是基于这一点差异它的适用场景稍微与run函数有点不一样。apply一般用于一个对象实例初始化的时候,需要对对象中的属性进行赋值。或者动态inflate出一个XML的View的时候需要给View绑定数据也会用到,这种情景非常常见。特别是在我们开发中会有一些数据model向View model转化实例化的过程中需要用到

mRootView = View.inflate(activity, R.layout.example_view, null)
mRootView.tv_cancel.paint.isFakeBoldText = true
mRootView.tv_confirm.paint.isFakeBoldText = true
mRootView.seek_bar.max = 10
mRootView.seek_bar.progress = 0
--------------------->
mRootView = View.inflate(activity, R.layout.example_view, null).apply {
   tv_cancel.paint.isFakeBoldText = true
   tv_confirm.paint.isFakeBoldText = true
   seek_bar.max = 10
   seek_bar.progress = 0
}

多层级判空问题

if (mSectionMetaData == null || mSectionMetaData.questionnaire == null || mSectionMetaData.section == null) {
    return;
}
if (mSectionMetaData.questionnaire.userProject != null) {
    renderAnalysis();
    return;
}
if (mSectionMetaData.section != null && !mSectionMetaData.section.sectionArticles.isEmpty()) {
    fetchQuestionData();
    return;
}
--------------->
mSectionMetaData?.apply {

    //mSectionMetaData不为空的时候操作mSectionMetaData

}?.questionnaire?.apply {

    //questionnaire不为空的时候操作questionnaire

}?.section?.apply {

    //section不为空的时候操作section

}?.sectionArticle?.apply {

    //sectionArticle不为空的时候操作sectionArticle

}

⑤、also函数

also函数的结构实际上和let很像唯一的区别就是返回值的不一样,let是以闭包的形式返回,返回函数体内最后一行的值,如果最后一行为空就返回一个Unit类型的默认值。而also函数返回的则是传入对象的本身

let写法

fun main() {
    val result = "carden".let {
        println(it.length)
        1000
    }
    println(result) // 打印:1000
}

also写法

fun main() {
    val result = "carden".also {
        println(it.length)
    }
    println(result) // 打印:carden
}

适用于let函数的任何场景,also函数和let很像,只是唯一的不同点就是let函数最后的返回值是最后一行的返回值而also函数的返回值是返回当前的这个对象。一般可用于多个扩展函数链式调用

总结:

通过以上几种函数的介绍,可以很方便优化kotlin中代码编写,整体看起来几个函数的作用很相似,但是各自又存在着不同。使用的场景有相同的地方比如run函数就是let和with的结合体.

结合实际项目场景,使用对应的扩展函数。ok,感谢阅读~~

本文参考来自:https://www.jianshu.com/p/884ca0a49e5e

------------------------------附录--------------------------------------

kotlin基础:

null声明:
"?"加在变量名后,系统在任何情况不会报它的空指针异常。
var othername : String="carden"
othername=null
println(othername?.length)
此时返回的是null
?:这个可以用作判断:例如
println(othername?.length?:-1)
当othername?.length不为null时返回length,否则为-1

"!!"加在变量名后,如果对象为null,那么系统一定会报异常!
var othername : String="carden"
othername=null
println(othername!!.length)
此时会报空指针异常

java
private String othername;
othername=null。
kotlin:
val othername:String ?=null
空判断:
java:
if(text!=null){
int length=text.length();
}
kotlin:
let:
text?.let{
val length=text.length
}

fun main() {
    val result = "Android轮子哥".let {
        println(it.length)
        1000
    }
    println(result)
}
最常用的场景就是使用let函数处理需要针对一个可null的对象统一做判空处理

 fun getCustom(custom: String): Array<String?>?{
            var numbes: Array<String?>?=null
            if (isBlank(custom)) {
                return null
            }
            if (!custom.contains("#")) {
                numbes= arrayOfNulls(1)
                for (i in numbes.indices) {
                    numbes[i] = custom
                }
                return numbes
            } else {
                val strs = custom.split("#")
                numbes = arrayOfNulls(strs.size)
                for (i in strs.indices) {
                    numbes[i] = strs[i]
                }
                return numbes
            }

        }


字符串拼接
Java
String firstName = "Amit";
String lastName = "Shekhar";
String message = "My name is: " + firstName + " " + lastName;
kotlin:
val firstName = "Amit"
val lastName = "Shekhar"
val message = "My name is: $firstName $lastName"
换行
Java
String text = "First Line\n" +
              "Second Line\n" +
              "Third Line";
Kotlin
val text = """
        |First Line
        |Second Line
        |Third Line
        """.trimMargin()
三元表达式:
Java
String text = x > 5 ? "x > 5" : "x <= 5";
kotlin:
val text = if (x > 5)
              "x > 5"
           else "x <= 5"
类型判断和转换 (声明式)
Java
if (object instanceof Car) {
}
Car car = (Car) object;
kotlin:
if(object is Car){
var car=object as Car
}
类型判断和转换 (隐式)
Java
if (object instanceof Car) {
   Car car = (Car) object;
}
kotlin:
if(object is Car){
var car =object
}
多重条件
java:
if (score >= 0 && score <= 300) { }
kotlin:
if(score in 0..300){}
更灵活的case语句
java:
int score = // some score;
String grade;
switch (score) {
    case 10:
    case 9:
        grade = "Excellent";
        break;
    case 8:
    case 7:
    case 6:
        grade = "Good";
        break;
    case 5:
    case 4:
        grade = "Ok";
        break;
    case 3:
    case 2:
    case 1:
        grade = "Fail";
        break;
    default:
        grade = "Fail";
}

kotlin:
var score= //some score
var grade=when(score){
   9, 10 -> "Excellent" 
    in 6..8 -> "Good"
    4, 5 -> "Ok"
    in 1..3 -> "Fail"
    else -> "Fail"
}
for循环
Java
for (int i = 1; i <= 10 ; i++) { }

for (int i = 1; i < 10 ; i++) { }

for (int i = 10; i >= 0 ; i--) { }

for (int i = 1; i <= 10 ; i+=2) { }

for (int i = 10; i >= 0 ; i-=2) { }

for (String item : collection) { }

for (Map.Entry<String, String> entry: map.entrySet()) { }

Kotlin:
for (i in 1..10) { }

for (i in 1 until 10) { }

for (i in 10 downTo 0) { }

for (i in 1..10 step 2) { }

for (i in 10 downTo 1 step 2) { }

for (item in collection) { }

for ((key, value) in map) { }

带下标的循环

 val array = arrayOf("a", "b", "c")
        for ((index,e) in array.withIndex()){
            println("下标=$index----元素=$e")
        }

更方便的集合操作
Java
final List<Integer> listOfNumber = Arrays.asList(1, 2, 3, 4);

final Map<Integer, String> keyValue = new HashMap<Integer, String>();
map.put(1, "Amit");
map.put(2, "Ali");
map.put(3, "Mindorks");

// Java 9
final List<Integer> listOfNumber = List.of(1, 2, 3, 4);

final Map<Integer, String> keyValue = Map.of(1, "Amit",
                                             2, "Ali",
                                             3, "Mindorks");
Kotlin
val listOfNumber = listOf(1, 2, 3, 4)
val keyValue = mapOf(1 to "Amit",
                     2 to "Ali",
                     3 to "Mindorks")
遍历
Java
// Java 7 and below
for (Car car : cars) {
  System.out.println(car.speed);
}

// Java 8+
cars.forEach(car -> System.out.println(car.speed));

// Java 7 and below
for (Car car : cars) {
  if (car.speed > 100) {
    System.out.println(car.speed);
  }
}

// Java 8+
cars.stream().filter(car -> car.speed > 100).forEach(car -> System.out.println(car.speed));
Kotlin
cars.forEach {
    println(it.speed)
}

cars.filter { it.speed > 100 }
      .forEach { println(it.speed)}

方法定义:
Java
void doSomething() {
   // logic here
}
//可扩展参数
void doSomething(int... numbers) {
   // logic here
}
Kotlin
fun doSomething() {
   // logic here
}

fun doSomething(vararg numbers: Int) {
   // logic here
}


带返回值的方法
Java
int getScore() {
   // logic here
   return score;
}
Kotlin
fun getScore(): Int {
   // logic here
   return score
}

// as a single-expression function
//如果指定值返回,可直接使用=
fun getScore(): Int = score

无结束符号
Java
int getScore(int value) {
    // logic here
    return 2 * value;
}
Kotlin
fun getScore(value: Int): Int {
   // logic here
   return 2 * value
}

// as a single-expression function

fun getScore(value: Int): Int = 2 * value
constructor 构造器
Java
public class Utils {

    private Utils() { 
      // This utility class is not publicly instantiable 
    }
    
    public static int getScore(int value) {
        return 2 * value;
    }
    
}
Kotlin
class Utils private constructor() {
//此方法类似java中的static,包裹的代码块可以直接调用
    companion object {
    
        fun getScore(value: Int): Int {
            return 2 * value
        }
        
    }
}

// other way is also there

object Utils {

    fun getScore(value: Int): Int {
        return 2 * value
    }

}

Get Set 构造器
Java
public class Developer {

    private String name;
    private int age;

    public Developer(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Developer developer = (Developer) o;

        if (age != developer.age) return false;
        return name != null ? name.equals(developer.name) : developer.name == null;

    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }

    @Override
    public String toString() {
        return "Developer{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
Kotlin
data class Developer(val name: String, val age: Int)

Java
public class Utils {

    private Utils() { 
      // This utility class is not publicly instantiable 
    }
    
    public static int triple(int value) {
        return 3 * value;
    }
    
}

int result = Utils.triple(3);

Kotlin
fun Int.triple(): Int {
  return this * 3
}

var result = 3.triple()

 
 
 kotlin中class文件与file的区别
 如果包含多个class,则为file
 
 class ExportFileDialog(val activity: SimpleActivity, val note: Note, val callback: (exportPath: String) -> Unit) {
 val callback: (exportPath: String) -> Unit)将函数callback作为参数。返回值unit类似java:void
 不过需要注意的是,作为参数的函数,该函数的参数类型和返回值类型一定要和规定的一致,比如:
 fun main() {
    foo2("xxx", this::bar2)     //right
    foo2("xxx", this::bar1)     //wrong, compile fail
}

fun bar1() {
    print("bar1")
}

fun foo2(content: String, body: (String) -> Unit) {
    body(content)
}

fun bar2(string: String) {
    print(string)
}

??by lazy本身是一种属性委托。属性委托的关键字是by。by lazy 的写法如下:

//用于属性延迟初始化
val name: Int by lazy { 1 }
 
//用于局部变量延迟初始化
public fun foo() {
    val bar by lazy { "hello" }
    println(bar)
以下以name属性为代表来讲解by kazy的原理,局部变量的初始化也是一样的原理。
??by lazy要求属性声明为val,即不可变变量,在java中相当于被final修饰。
??这意味着该变量一旦初始化后就不允许再被修改值了(基本类型是值不能被修改,对象类型是引用不能被修改)。{}内的操作就是返回唯一一次初始化的结果。
??by lazy可以使用于类属性或者局部变量。

还有值得注意的地方:kotlin.NotImplementedError: An operation is not implemented: not implemented

实现接口时,默认会生成这句话:TODO("not implemented"),在Android中这个是无所谓的,但是来到kotlin就不一样了,如果你在某个函数的第一行添加TODO的话,那么很抱歉,它不会跳过,然后运行下一行代码。那如果真要添加TODO的话,那就只能在函数的最后一行添加了。如果非要添加的话,可以在最后一行添加。或者直接去掉就ok啦。

在java中,我们常常会这么写:

Class<?> cls  

在kotlin中就不可行,kotlin的写法为:cls:Class<*>


--------------------- 

Kotlin 泛型中的 in 和 out

in & out 怎么记?
out(协变)

如果泛型类只将泛型类型作为函数的返回(输出),那么使用 out:

interface Production<out T> {
    fun produce(): T
}

可以称之为生产类/接口,因为它主要是用来生产(produce)指定的泛型对象。因此,我们可以简单地这样记忆:

produce = output = out

in(逆变)

如果泛型类只将泛型类型作为函数的入参(输入),那么使用 in:

interface Consumer<in T> {
    fun consume(item: T)
}

可以称之为消费者类/接口,因为它主要是用来消费(consume)指定的泛型对象。因此我们可以简单地这样记忆:

consume = input = in

invariant(不变)

如果泛型类既将泛型类型作为函数参数,又将泛型类型作为函数的输出,那么既不用 out 也不用 in:

interface ProductionConsumer<T> {
    fun produce(): T
    fun consume(item: T)
}

如何传递泛型class,inline与reified相结合,

我们知道在java中使用泛型的时候,无法通过泛型来得到Class,一般我们会将Class通过参数传过去。
在kotlin中一个内联函数(inline)可以被具体化(reified),这意味着我们可以得到使用泛型类型的Class

例如retrofit中需要传递一个service作为class,可以如下:

inline fun <reified T> create() = getRetrofit().create(T::class.java)

调用:val api by lazy { RetrofitService.create<ApiService>() }

--------------------------------------kotin复合符号('?.' '?:' '!!' 'as?' '?' )-------------------------------

1、 ?.安全调用符

if (foo != null){
    return foo.bar()
}else{

    return null
}

2、 ?:

3、 as?

4、 !!

5、 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值