Kotlin 基础 | 拒绝语法噪音

简介: Kotlin 基础 | 拒绝语法噪音

程序员最头痛的事莫过于看不懂别人的代码。缘由是各式各样的,但归结于一点就是复杂度太高。Kotlin 在降低代码复杂度上下了大功夫,运用一系列新的语法特性降低语法噪音,以求更简单直白地表达语义。


这篇以一个刚从 Java 转到 Kotlin 程序员的视角分享下 Kotlin 给我的第一印象。


new  分号类型


新建对象不需要new关键词。


任何语句的结尾不需要; 但加上也不会有语法错误。


//java
StringBuffer buffer = new StringBuffer();
//kotlin
var buffer = StringBuffer()


  • varkotlin保留字,用于声明变量。与之对应的是val用于声明常量,常量意思是引用不可变,但并不代表其引用对象也不可变。


  • 不需要显示指明变量类型,因为kotlin会根据上下文推断变量类型,这种能力称为 “类型推导”


  • 可以通过下面的语法来指定类型:

var buffer: StringBuffer = StringBuffer()


  • kotlin中类型是后置的,在变量名后跟上: 类型就可以显示指定类型。同理,它也用于指定函数返回值类型:


fun getMessage(): String{
    return "message"
}


  • fun关键字用于声明函数。


implementsextends@Override


//java
public class CheckableActivity extends Activity {
    final public void setStatus(){}
}
public class MyListener implements View.OnClickListener{
    @Override
    public void onClick(View v) {
    }
}
//kotlin
class CirclePartyListActivity : Activity() {
    fun setStatus(){}
}
class MyListener : View.OnClickListener{
    override fun onClick(v: View?) {
    }
}


  • kotlin: 取代了implementsextends保留字。


  • @Override也被override保留字取代并且和函数头同行,kotlin中的override是必须的,而java中是可选的。


  • kotlin中类和方法默认是final的(可省略不写),这意味着默认情况下,类和方法是不允许被继承和重写的(这是为了防止脆弱的基类,即对基类方法的修改会导致子类出现预期之外的行为)。只有通过open保留字显示声明该类或方法可以被继承或重写:


open class A{
    open fun do(){
    }
}


()


kotlinlambda也更加简约:


//正常情况
view.setOnClickListener({ v -> v.setVisibility(View.INVISIBLE) })
//当lambda是函数的最后一个参数时,可以将其移到括号外面
view.setOnClickListener() { v -> v.setVisibility(View.INVISIBLE) }
//当函数只有一个lambda类型的参数,可以去省去括号
view.setOnClickListener { v -> v.setVisibility(View.INVISIBLE) }
//当lambda只有一个参数,可省去参数列表,在表达式部分用it引用参数
view.setOnClickListener { it.setVisibility(View.INVISIBLE) }


gettersetter


java中,字段和其访问器的组合被称为属性,kotlin引入了property access syntax,它取代了字段和访问器方法,用这种方式进一步简化上面的代码:


view.setOnClickListener { it.visibility = View.INVISIBLE }


  • 所有被定义了gettersetter方法的字段,在kotlin中都可以通过赋值语法来操作。


{ }return


kotlin中的语句和表达式的唯一区别是:表达式有值,而语句没有。如果函数体由单个表达式构成,可以省去花括号和return,并用赋值的=表示将表达式的值赋值给返回值,这种语法叫表达式函数体


//java
public int add(int a, int b){
    return a+b ;
}
//kotlin
fun add(a: Int, b: Int): Int = a+b


在 lambda 表达式中包含多条语句或表达式时,若省略return,则默认将最后一个表达式的值作为返回值:


view.setOnTouchListener { v, event ->
    ...//do something
    false
}


上述代码表示在OnTouchListener.onTouch()中返回 false。


switch-case-break


//java
String color;
switch(colorInt){
    case Color.RED:
        color = "red";
        break;
    case Color.BLUE:
        color = "blue";
        break;
    default:
        color = "black";
        break;
}
//kotlin
val color = when (colorInt) {
    Color.RED -> "red"
    Color.BLUE -> "blue"
    else -> "black"
}


  • when用于取代switch-case,不需要在每个分支末尾调用break,如果有一个分支命中则会立即返回。


  • when是一个表达式,这意味着它有返回值,返回值等于命中分支中最后一条语句的返回值。


default


java中的default保留字用于接口中默认方法的实现。在kotlin中可以省去它。


//java
public interface IMessage {
    default String getMessage() {
        return "default message";
    }
    int getMessageId();
}
//kotlin
interface IMessage {
    fun getMessage(): String {
        return "default message"
    }
    fun getMessageId(): Int
}


  • Intjava中基本数据类型int的包装类,kotlin中没有基本数据类型。


防御式编程


//java
public class Address {
    private String country;
    public String getCountry() {
        return country;
    }
}
public class Company {
    private Address address;
    public Address getAddress() {
        return address;
    }
}
public class Person {
    private Company company;
    public String getCountry() {
        String country = null;
        //多次防御式编程
        if (company != null) {
            if (company.getAddress() != null) {
                country = company.getAddress().getCountry();
            }
        }
        return country;
    }
}
//kotlin
fun Person.getCountry(): String? {
    return this.company?.address?.country
}


  • ?.称为 安全调用运算符 ,它把判空检查和一次方法调用合并成一个操作。只有当调用变量不为null时,才会执行调用,否则整个表达式返回null。这意味着,不再需要防御式编程。


  • ?置于类型之后表示这个类型可空,上面的函数声明表示此函数的返回值可能为null


  • 上面的 kotlin 代码为Person类添加了一个getCountry()方法,这种技术叫扩展函数


扩展函数


扩展函数是一个类的成员函数,但它定义在类体外面。这样定义的好处是,可以在任何时候任何地方给类添加功能。


在扩展函数中,可以像类的其他成员函数一样访问类的属性和方法(除了被privateprotected修饰的成员)。还可以通过this引用类的实例,也可以省略它,把上段代码进一步简化:


fun Person.getCountry(): String? {
    return company?.address?.country
}


kotlin预定了很多扩展函数,下面就会用到其中的apply


冗余对象名


编程中经常会遇到“对同一个对象做多次操作”的场景,比如:


Intent intent = new Intent(this, Activity1.class);
intent.setAction("actionA");
Bundle bundle = new Bundle();
bundle.putString("content","hello");
bundle.putString("sender","taylor");
intent.putExtras(bundle);
startActivity(intent);


其中,对象intentbundle重复出现若干次,这对于极简主义的kotlin来说不能忍,它的表达方式如下:


Intent(this,Activity1::class.java).apply {
    action = "actionA"
    putExtras(Bundle().apply {
        putString("content","hello")
        putString("sender","taylor")
    })
    startActivity(this)
}


其中,apply的定义如下:


//为泛型T对象添加新功能apply(),它接受一个lambda类型的参数block,且lambda调用的发起者是对象本身
public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    //执行lambda
    block()
    //返回调用者自身
    return this
}


对于object.apply{lambda}可以简单的理解为:在object对象上应用lambda操作,并且最终返回object对象本身。所以上述代码也可以写成更加紧凑的形式:


startActivity(Intent(this, Activity1::class.java).apply {
    action = "actionA"
    putExtras(Bundle().apply {
        putString("content", "hello")
        putString("sender", "taylor")
    })
})


同一类型的预定义扩展函数还包括withletalso。它们的共同点是适用于 “对同一个对象做多次操作” 的场景 。它们的不同点总结如下:


函数 返回值 调用者角色 如何引用调用者
also 调用者本身 作为lambda参数 it
apply 调用者本身 作为lambda接收者 this
let lambda返回值 作为lambda参数 it
with lambda返回值 作为lambda接收者 this


  • kotlin中,发起调用扩展函数的那个对象,叫接收者对象。同理,发起调用lambda的对象叫做lambda接收者


  • 可以将also的源码和apply做对比,更好的理解他们调用者角色的差别:


//为泛型T对象添加新功能also(),它接受一个lambda类型的参数block,且对象是lambda的参数
public inline fun <T> T.also(block: (T) -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block(this)
    return this
}


综合应用


“让 app 中所有被点击的 View 都带上缩放动画”。综合运用上述kotlin知识点实现这个需求之前,先来看看java是如何实现的:


  1. 先定义工具类,该工具类为传入的View分别设置触摸和单击监听器。在按下时播放动画,松手时反向播放动画。


public class ViewUtil {
    public static void addExtraAnimClickListener(View view, ValueAnimator animator, View.OnClickListener listener) {
        view.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        animator.start();
                        break;
                    case MotionEvent.ACTION_CANCEL:
                    case MotionEvent.ACTION_UP:
                        animator.reverse();
                        break;
                }
                //若返回true,则屏蔽了点击事件
                return false;
            }
        });
        view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (listener != null) {
                    listener.onClick(v);
                }
            }
        });
    }
}


  1. 在界面中新建动画和点击响应逻辑并将它们传递给工具类


Button btn3 = findViewById(R.id.btn3);
//新建动画:变大
ValueAnimator animator = ValueAnimator.ofFloat(1.0f, 1.2f);
animator.setDuration(100);
animator.setInterpolator(new AccelerateInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        Float value = ((Float) animation.getAnimatedValue());
        btn3.setScaleX(value);
        btn3.setScaleY(value);
    }
});
//点击响应逻辑
View.OnClickListener onClickListener = new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Toast.makeText(Activity1.this, "spring anim", Toast.LENGTH_LONG).show();
    }
};
//应用工具类
ViewUtil.addExtraAnimClickListener(btn3, animator, onClickListener);


不要眨眼,换kotlin闪亮登场:


  1. 给View添加扩展函数


//扩展函数接收一个动画和一个点击响应逻辑(用lambda表示)
fun View.extraAnimClickListener(animator: ValueAnimator, action: (View) -> Unit) {
    setOnTouchListener { v, event ->
        when (event.action) {
            MotionEvent.ACTION_DOWN -> animator.start()
            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> animator.reverse()
        }
        false
    }
    setOnClickListener { action(this) }
}


  1. 应用扩展函数


btnSpringAnim.extraAnimClickListener(ValueAnimator.ofFloat(1.0f, 1.15f).apply {
    interpolator = AccelerateInterpolator()
    duration = 100
    addUpdateListener {
        btnSpringAnim.scaleX = it.animatedValue as Float
        btnSpringAnim.scaleY = it.animatedValue as Float
    }
}) { Toast.makeText(this, "spring anim", Toast.LENGTH_LONG).show() }


  • btnSpringAnim是一个Button控件的id(只要装了kotlin插件,就不需要findViewById())


  • as保留字用于类型强制转换。


  • 是不是有一种 “白话文转文言文” 的感觉,kotlin凭借着极强的表达力用将近 1/3 的代码量完成了功能。


知识点总结


  • var保留词用于声明变量,val保留词用于声明常量。大多数情况下不需要显示指明变量类型,kotlin 具有类型推导能力,会根据上下文自动推断类型。


  • fun保留字用于声明函数。


  • override保留字表示重写父类方法或者实现接口中的抽象方法,与 java 不同的是,它必须显示出现在重写方法前( java 允许省略)。


  • as保留字用于类型强制转换。


  • kotlin 中类型是后置的,在变量名或函数参数列表后跟上: 类型就可以显示指定类型。


  • :还用于继承类(取代extends)、实现接口(取代implements)。


  • 新建对象时不需要new,而是直接调用构造函数。


  • 语句末尾不需要; 但加上也不会有语法错误。


  • kotlin 中类和方法默认是final的,他们不能被继承和重写。只有通过加上open后才能被继承和重写。


  • kotlin 中没有基本数据类型,而是用其对应的包装类表示。


  • 给接口方法添加默认实现时不需要default关键字。


  • kotlin 中的语句和表达式的唯一区别是:表达式有值,而语句没有。


  • 如果函数体由单个表达式构成,可以省去花括号和return。


  • when保留字用于取代switch-case,而且它是一个表达式,返回值是命中分支中最后一表达式的值。


  • kotlin 引入了property access syntax,不再需要getter和setter方法,可以直接对属性赋值。


  • ?.称为 安全调用运算符 ,只有当调用变量不为null时,才会执行调用,否则整个表达式返回null。这样就避免了防御式编程。


  • ?置于类型之后表示这个类型的变量或返回值值可能为null


  • kotlin 使用扩展函数,可以在类体外给类新增方法。


  • kotlin 预定了很多扩展函数,其中有一类适用于“对同一个对象做多次操作”。包括also()apply()let()with()


最近开始学习 kotlin ,研读《Kotlin实战》的同时,在项目中加以运用。这个系列会不断地新增来自书本和实践中的新发现。希望对你能有所帮助~~


推荐阅读











目录
相关文章
|
XML 自然语言处理 Java
Kotlin 使用DSL构建语法结构 看这一篇就够了~
Kotlin 使用DSL构建语法结构 看这一篇就够了~
1720 0
Kotlin 使用DSL构建语法结构 看这一篇就够了~
|
8天前
|
安全 Java Linux
Kotlin 编程语言详解:特点、应用领域及语法教程
**Kotlin**是JetBrains于2016年推出的编程语言,兼容Java,广泛用于移动(尤其是Android)、Web、服务器及数据科学。它的语法简洁安全,支持多平台,学习曲线平缓,且拥有活跃的社区。`main()`函数是程序入口,`println()`用于输出,而`print()`不换行。注释以`//`开始单行,`/* */`包裹多行
20 0
|
1月前
|
人工智能 Dart Java
Kotlin基础语法
Kotlin基础语法
|
1月前
|
XML Java 开发者
预测一下 Kotlin 未来会有哪些新语法
预测一下 Kotlin 未来会有哪些新语法
42 0
|
安全 前端开发 Java
Kotlin Primer · 第一章 · 基础语法与面向对象思想
Kotlin Primer · 第一章 · 基础语法与面向对象思想
61 0
Kotlin Primer · 第一章 · 基础语法与面向对象思想
|
NoSQL MongoDB 数据库
开心档-软件开发入门之Kotlin 基础语法
前言 目录 Kotlin 基础语法 包声明 默认导入 函数定义 可变长参数函数 lambda(匿名函数) 定义常量与变量 注释 字符串模板 NULL检查机制 类型检测及自动类型转换 区间 实例测试 实例 本章将会讲解 Kotlin 基础语法 在插入文档时,MongoDB 首先检查固定集合的 size 字段,然后检查 max 字段。
|
安全 Java 编译器
开心档-Kotlin 基础语法
kotlin源文件不需要相匹配的目录和包,源文件可以放在任何文件目录。 以上例中 test() 的全名是 com.kxdang.main.test、Runoob 的全名是 com.kxdang.main.Runoob。 如果没有指定包,默认为 default 包。
开心档-Kotlin 基础语法
|
JSON Java API
一次有趣的 Kotlin 语法解析实践
一次有趣的 Kotlin 语法解析实践
227 0
|
Java Kotlin
【Kotlin】Lambda 表达式 ( 简介 | 表达式语法 | 表达式类型 | 表达式返回值 | 调用方式 | 完整示例 )
【Kotlin】Lambda 表达式 ( 简介 | 表达式语法 | 表达式类型 | 表达式返回值 | 调用方式 | 完整示例 )
265 0
【Kotlin】Lambda 表达式 ( 简介 | 表达式语法 | 表达式类型 | 表达式返回值 | 调用方式 | 完整示例 )
|
Kotlin Java Scala
带你读《Kotlin核心编程》之二:基础语法
本书不是一本简单介绍Kotlin语法应用的图书,而是一部专注于帮助读者深入理解Kotlin的设计理念,指导读者实现Kotlin高层次开发的实战型著作。书中深入介绍了Kotlin的核心语言特性、设计模式、函数式编程、异步开发等内容,并以Android和Web两个平台为背景,演示了Kotlin的实战应用。