作者:唐子玄 链接:https://juejin.cn/post/6963054983067467790
程序员最头痛的事莫过于看不懂别人的代码。缘由是各式各样的,但归结于一点就是复杂度太高。Kotlin 在降低代码复杂度上下了大功夫,运用一系列新的语法特性降低语法噪音,以求更简单直白地表达语义。
这篇以一个刚从 Java 转到 Kotlin 程序员的视角分享下 Kotlin 给我的第一印象。
新建对象不需要new关键词。
任何语句的结尾不需要; 但加上也不会有语法错误。
//java
StringBuffer buffer = new StringBuffer();
//kotlin
var buffer = StringBuffer()
var buffer: StringBuffer = StringBuffer()
fun getMessage(): String{
return "message"
}
//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?) {
}
}
open class A{
open fun do(){
}
}
kotlin的lambda也更加简约:
//正常情况
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) }
java中,字段和其访问器的组合被称为属性,kotlin引入了property access syntax,它取代了字段和访问器方法,用这种方式进一步简化上面的代码:
view.setOnClickListener { it.visibility = View.INVISIBLE }
所有被定义了getter和setter方法的字段,在kotlin中都可以通过赋值语法来操作。
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。
//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"
}
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
}
Int是java中基本数据类型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
}
扩展函数是一个类的成员函数,但它定义在类体外面。这样定义的好处是,可以在任何时候任何地方给类添加功能。
在扩展函数中,可以像类的其他成员函数一样访问类的属性和方法(除了被private和protected修饰的成员)。还可以通过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);
其中,对象intent和bundle重复出现若干次,这对于极简主义的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")
})
})
同一类型的预定义扩展函数还包括with、let、also。它们的共同点是适用于 “对同一个对象做多次操作” 的场景 。它们的不同点总结如下:
//为泛型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是如何实现的:
先定义工具类,该工具类为传入的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);
}
}
});
}
}
在界面中新建动画和点击响应逻辑并将它们传递给工具类
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闪亮登场:
//扩展函数接收一个动画和一个点击响应逻辑(用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) }
}
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() }