1.前言在今年5月的GoogleI/O上,Google正式向全世界宣布了Kotlin-First。一个重要的概念,Kotlin将成为Android开发者的首选语言。新语言有新特性,开发者还是保持Java编程习惯去写Kotlin,这也不是不可以,但总感觉几乎没有意义。最近公众号「GoogleDevelopers」连载了一个系列文章《实用 Kotlin 构建 Android 应用 | Kotlin 迁移指南》,其中举例说明了一些Kotlin编码技巧。既然是指导性文章,自然会在“多而广”的基础上,故意省略一些细节,同时,例子中可能会有一些不恰当的场景。在这里,我将填写这些详细信息。今天就说说利用Kotlin的方法默认参数的特性,来实现类似Java的方法重载的效果。全面分析这个特性的用法和原理,以及使用过程中的一个深坑。2.Kotlin的简单方法重载2.1Kotlin是如何简化方法重载的?在Java中,我们可以在同一个类中定义多个同名的方法,只要每个方法的参数类型或参数个数不同即可,这就是Java的方法重载。classHello{publicstaticvoidhello(){System.out.println("Hello,world!");}publicstaticvoidhello(Stringname){System.out.println("Hello,"+name+"!");}publicstaticvoidhello(Stringname,intage){if(age>0){System.out.println("你好,"+name+"("+age+")!");}else{System.out.println("你好,"+name+"!");}}}在这个例子中,我们定义了三个同名的hello()方法,但具有不同的逻辑细节。在Kotlin中,因为它支持在相同的方法中,传递“?”标记可为空的参数,并通过“=”给出参数的默认值。那么这三个方法在Kotlin中可以软化为一个方法。objectHelloDemo{funhello(name:String="world",age:Int=0){if(age>0){System.out.println("你好,${name}(${age})!");}else{System.out.println("Hello,${name}!");}}}是在Kotlin类中调用的,和前面的Java实现一致。HelloDemo.hello()HelloDemo.hello("呈祥魔影")HelloDemo.hello("呈祥魔影",16)但是这个方法声明的是Kotlin方法参数的默认值,在Java类中使用时,有一些区别.因为HelloDemo类被声明为对象,所以需要在Java中使用INSTANCE来调用它的方法。HelloDemo.INSTANCE.hello("呈象魔影",16);在Kotlin中调用hello()方法非常方便,可以选择性的忽略参数,但在Java中使用时,必须显式地完整赋值参数。Kotlin和Java分别是这样声明使用参数默认值的方法的。接下来我们来看一下原理。2.2Kotlin方法参数指定默认值的原则用Kotlin编写的代码之所以能在基于Java的虚拟机中运行,主要是因为在编译过程中会被编译成虚拟机可以识别的Java字节码.所以我们可以通过两次转换(ShowKotlinBytecode+Decompile)得到对应的Kotlin生成的Java代码。publicfinalvoidhello(@NotNullStringname,intage){Intrinsics.checkParameterIsNotNull(name,"name");if(age>0){System.out.println("Hello,"+name+'('+age+")!");}else{System.out.println("Hello,"+name+'!');}}//$FF:syntheticmethodpublicstaticvoidhello$default(HelloDemovar0,Stringvar1,intvar2,intvar3,Objectvar4){if((var3&1)!=0){var1="world";}if((var3&2)!=0){var2=0;}var0.hello(var1,var2);}会在这里生成一个hello()方法,同时也会有一个合成方法(syntheticmethod)hello$default,用来处理默认参数的问题。在编译期间,在Kotlin中调用hello()方法将有选择地自动替换为名为hello()的合成方法。//Kotlin调用HelloDemo.hello()HelloDemo.hello("呈象魔影")HelloDemo.hello("呈象魔影",16)//编译后的Java代码HelloDemo.hello$default(HelloDemo.INSTANCE,(String)null,0,3,(Object)null);HelloDemo.hello$default(HelloDemo.INSTANCE,"成象魔影",0,2,(Object)null);HelloDemo.INSTANCE.hello("成"象魔影",16);注意例子的最后,使用hello(name,age)方法重载时,其实和Java中的调用是一致的,没什么好说的。这就是Kotlin方法重载时使用指定默认参数的方法的原理,省去了多个方法重载代码。了解了原理后发现确实减少了我们写的代码量,但是有没有什么场景需要显式重载这些方法呢?自然是有的,比如自定义View的时候。3.自定义View的构造方法满足Kotlin3.1也是一种方法。回到上面提到的谷歌开发者的《实用 Kotlin 构建 Android 应用 | Kotlin 迁移指南》系列文章,给出的例子其实是很不恰当的。这里的例子中使用了View这个词,重载的方法都是View的构造方法。我们在自定义View的时候,经常会和这三个方法打交道。但是谷歌工程师在这里给出的例子很容易被误解。其实如果在自定义View的时候这样写,肯定会报错。比如我们自定义一个DemoView,它继承自EditView。classDemoView(context:Context,attrs:AttributeSet?=null,defStyleAttr:Int=0):EditText(context,attrs,defStyleAttr){}这个自定义的DemoView,用在xml布局的时候,虽然编译不会出错,但是运行时,您将获得NoSuchMethodException。Causedby:java.lang.NoSuchMethodException:
