Kotlin系列之消除函数重载

标签: kotlin      消除函数重载


目录:

  • 一、函数重载的带来问题
  • 二、Kotlin中的函数命名参数
  • 三、Kotlin中的函数默认值参数
  • 四、@JvmOverloads注解解决Java调用Kotlin重载函数问题
  • 五、需要注意的问题

一、函数重载的带来问题

  • 1、遇到问题

    无论是在Java或者C++中都有函数重载一说,函数重载目的为了针对不同功能业务需求,然后暴露不同参数的接口,包括参数列表个数,参数类型,参数顺序。但是这些参数不同的调整无疑会增加更多同名的函数,对于外部调用者特别的confused. 很容易出现调用错误,特别当存在参数个数相同,只是参数类型不同,就很容易出错。

    请看以下例子(以Thread类为例):

    public class Thread{
    
     public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
     }
     
     public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
     }
     
     public Thread(ThreadGroup group, Runnable target) {
        init(group, target, "Thread-" + nextThreadNum(), 0);
     }
     
     public Thread(ThreadGroup group, Runnable target, String name) {
        init(group, target, name, 0);
     }
    
    
    }
    
    class ThreadTest{
       public static void main(String[] args){
          Thread thread1 = new Thread();
          
          Thread thread2 = new Thread(new Runnable() {
    		@Override
    		public void run() {//todo
    
    		}
    	});
    	
    	  Thread thread3 = new Thread(null, new Runnable() {
    		@Override
    		public void run() {//todo
    
    		}
    	});
    	
           Thread thread4 = new Thread(null, new Runnable() {
    		@Override
    		public void run() {//todo
    
    		}
    	}, "thread_test")  
       }
    }
    
    
  • 2、解决问题

    由以上的问题,我们知道在Java中会出现很多重载方法,如果调用错误了,也许我们会把这个问题归结于自身不够严谨,可谁曾想过如果一门好的语言是能够在语法上就很明确并且在语法上减少一些错误。然而Kotlin这门语言就在方面优越于Java,它语法的层面上就降低了代码犯错可能。

解决以上问题就是利用Kotlin中的函数命名参数和默认值参数就能彻底消除函数重载的问题。

Kotlin解决例子:

 class Thread(group: ThreadGroup? = null,
                   target: Runnable? = null,
                       name: String? = null)
  : Runnable{
      init(group, target, name, 0)
 }
 
 fun main(agrs: Array){
    val thread1= Thread()
    val thread2 = Thread(target = object: Runnable{
          override fun run() {
				//todo
	    }
      })
    val thread3 = Thread(target = object: Runnable{
          override fun run() {
				//todo
	    }
      }, name = "therad_test")  
 }

二、Kotlin中的函数命名参数

我们需要关注的第一个问题就是函数的可读性,然而函数命名参数的出现就是为了解决函数的可读性,准确来说是为了让调用者更加具有可读性。

  • 定义

    在Kotlin中,当调用一个Kotlin定义的函数时,可以显示地标明一些参数的名称,而且可以打乱顺序参数调用顺序,因为可以通过参数名称就能唯一定位具体对应参数。

  • Java中调用的例子

    //定义一个Java函数
    public static String joinToString(Collection collection, String prefix, String seperataor, String postfix){
     //...
    }
    
    //调用
    joinToString(collection, " ", " "," ")
    

    由以上Java中调用joinToString方法其实就比较confused,特别是后面三个空格但从调用的地方根本就无法知道joinToString方法对应的参数是哪个,每个值具体意思是什么,那么就值得你去尝试一下kotlin了。

  • Kotlin中调用的例子

//函数使用默认值参数 可以消除重载
@JvmOverloads
fun joinString(
collection: Collection,
separator: String,
prefix: String,
postfix: String
): String {
return collection.joinToString(separator, prefix, postfix)
}

//调用
fun main(args: Array) {
println(joinString(collection = listOf(1, 2, 3, 4), separator = “%”, prefix = “<”, postfix = “>”))
}

> 实际上单单靠命名参数是不能解决函数重载的,命名函数只是纯粹地为了函数调用更加明确和调用者传入的值可以与声明函数的参数对应。要想解决函数重载问题得靠默认值参数来解决  

三、Kotlin中的函数默认值参数

> 当我们在处理默认值参数的时候,命名参数就十分有用,并且一般都使用两者结合,然后解决函数重载的问题。在Kotlin中,可以在声明函数的时候,指定参数的默认值,就可以避免函数的重载。如果再结合使用命名参数,可以省略中间的一些参数,也可以以你想要的任意顺序只给定你需要的参数。

//函数使用默认值参数 可以消除重载
@JvmOverloads
fun joinString(
collection: Collection = listOf(),
separator: String = “,”,
prefix: String = “”,
postfix: String = “”
): String {
return collection.joinToString(separator, prefix, postfix)
}

//调用
fun main(args: Array) {
//函数使用命名参数可以提高代码可读性
println(joinString(collection = listOf(1, 2, 3, 4), separator = “%”, prefix = “<”, postfix = “>”))
println(joinString(collection = listOf(1, 2, 3, 4), prefix = “<”, postfix = “>”))
println(joinString(collection = listOf(1, 2, 3, 4), separator = “!”, prefix = “<”))
println(joinString(collection = listOf(1, 2, 3, 4), separator = “!”, postfix = “>”))
println(joinString(collection = listOf(1, 2, 3, 4), separator = “!”))
println(joinString(collection = listOf(1, 2, 3, 4), prefix = “<”))
println(joinString(collection = listOf(1, 2, 3, 4), postfix = “>”))
println(joinString(collection = listOf(1, 2, 3, 4)))
}

运行结果:

<1%2%3%4>
<1,2,3,4>
<1!2!3!4]
[1!2!3!4>
[1!2!3!4]
<1,2,3,4]
[1,2,3,4>
[1,2,3,4]

Process finished with exit code 0



四、@JvmOverloads注解解决Java调用Kotlin重载函数问题

> 由于在Java中是没有默认值参数的概念,当我们需要从Java中调用Kotlin中的默认值重载函数的时候,必须显示的指定所有参数值。但是这个绝对不是我们想要,否则Kotlin就失去了重载的意义了不能和Java完全互操作。所以在Kotlin给出了另一个方案就是使用@JvmOverloads注解这样就会自动生成多个重载方法供Java调用。我可以通过Kotlin代码来看下以下例子 - kotlin代码:

@JvmOverloads
fun joinString(
collection: Collection = listOf(),
separator: String = “,”,
prefix: String = “”,
postfix: String = “”
): String {
return collection.joinToString(separator, prefix, postfix)
}

- 反编译代码成Java代码: 

// $FF: synthetic method
// F F : b r i d g e m e t h o d @ J v m O v e r l o a d s @ N o t N u l l p u b l i c s t a t i c S t r i n g j o i n S t r i n g FF: bridge method @JvmOverloads @NotNull public static String joinString FF:bridgemethod@JvmOverloads@NotNullpublicstaticStringjoinStringdefault(Collection var0, String var1, String var2, String var3, int var4, Object var5) {
if((var4 & 1) != 0) {
var0 = (Collection)CollectionsKt.emptyList();
}

  if((var4 & 2) != 0) {
     var1 = ",";
  }

  if((var4 & 4) != 0) {
     var2 = "";
  }

  if((var4 & 8) != 0) {
     var3 = "";
  }

  return joinString(var0, var1, var2, var3);

}

@JvmOverloads
@NotNull
public static final String joinString(@NotNull Collection collection, @NotNull String separator, @NotNull String prefix) {
return joinString$default(collection, separator, prefix, (String)null, 8, (Object)null);
}

@JvmOverloads
@NotNull
public static final String joinString(@NotNull Collection collection, @NotNull String separator) {
return joinString$default(collection, separator, (String)null, (String)null, 12, (Object)null);
}

@JvmOverloads
@NotNull
public static final String joinString(@NotNull Collection collection) {
return joinString$default(collection, (String)null, (String)null, (String)null, 14, (Object)null);
}

@JvmOverloads
@NotNull
public static final String joinString() {
return joinString$default((Collection)null, (String)null, (String)null, (String)null, 15, (Object)null);
}



五、需要注意的问题

- 1、在Kotlin中函数使用命名参数需要注意的一点就是,即时在Java重载了很多构造器方法或者普通方法,在Kotlin中调用Java中的方法是不能使用命名参数的,不管你是JDK中的函数或者是Android框架中的函数都是不允许使用命名参数的。 - 2、在Kotlin中参数的默认值是被编译到被调用的函数中的,而不是调用的地方,所以改变了默认值后需要重新编译这个函数。我们可以从如下反编译代码可以看出Kotlin是把默认值编译进入了函数内部的。

@JvmOverloads
@NotNull
public static String joinString$default(Collection var0, String var1, String var2, String var3, int var4, Object var5) {
if((var4 & 1) != 0) {
var0 = (Collection)CollectionsKt.emptyList();
}

if((var4 & 2) != 0) {
var1 = “,”;
}

if((var4 & 4) != 0) {
var2 = “”;
}

if((var4 & 8) != 0) {
var3 = “”;
}

return joinString(var0, var1, var2, var3);
}
``

欢迎关注Kotlin开发者联盟,这里有最新Kotlin技术文章,每周会不定期翻译一篇Kotlin国外技术文章。如果你也喜欢Kotlin,欢迎加入我们~~~

Kotlin系列文章,欢迎查看:

Kotlin邂逅设计模式系列:

  • 当Kotlin完美邂逅设计模式之单例模式(一)

数据结构与算法系列:

  • 每周一算法之二分查找(Kotlin描述)

翻译系列:

  • [译] Kotlin中关于Companion Object的那些事
  • [译]记一次Kotlin官方文档翻译的PR(内联类)
  • [译]Kotlin中内联类的自动装箱和高性能探索(二)
  • [译]Kotlin中内联类(inline class)完全解析(一)
  • [译]Kotlin的独门秘籍Reified实化类型参数(上篇)
  • [译]Kotlin泛型中何时该用类型形参约束?
  • [译] 一个简单方式教你记住Kotlin的形参和实参
  • [译]Kotlin中是应该定义函数还是定义属性?
  • [译]如何在你的Kotlin代码中移除所有的!!(非空断言)
  • [译]掌握Kotlin中的标准库函数: run、with、let、also和apply
  • [译]有关Kotlin类型别名(typealias)你需要知道的一切
  • [译]Kotlin中是应该使用序列(Sequences)还是集合(Lists)?
  • [译]Kotlin中的龟(List)兔(Sequence)赛跑

原创系列:

  • 教你如何完全解析Kotlin中的类型系统
  • 如何让你的回调更具Kotlin风味
  • Jetbrains开发者日见闻(三)之Kotlin1.3新特性(inline class篇)
  • JetBrains开发者日见闻(二)之Kotlin1.3的新特性(Contract契约与协程篇)
  • JetBrains开发者日见闻(一)之Kotlin/Native 尝鲜篇
  • 教你如何攻克Kotlin中泛型型变的难点(实践篇)
  • 教你如何攻克Kotlin中泛型型变的难点(下篇)
  • 教你如何攻克Kotlin中泛型型变的难点(上篇)
  • Kotlin的独门秘籍Reified实化类型参数(下篇)
  • 有关Kotlin属性代理你需要知道的一切
  • 浅谈Kotlin中的Sequences源码解析
  • 浅谈Kotlin中集合和函数式API完全解析-上篇
  • 浅谈Kotlin语法篇之lambda编译成字节码过程完全解析
  • 浅谈Kotlin语法篇之Lambda表达式完全解析
  • 浅谈Kotlin语法篇之扩展函数
  • 浅谈Kotlin语法篇之顶层函数、中缀调用、解构声明
  • 浅谈Kotlin语法篇之如何让函数更好地调用
  • 浅谈Kotlin语法篇之变量和常量
  • 浅谈Kotlin语法篇之基础语法

Effective Kotlin翻译系列

  • [译]Effective Kotlin系列之考虑使用原始类型的数组优化性能(五)
  • [译]Effective Kotlin系列之使用Sequence来优化集合的操作(四)
  • [译]Effective Kotlin系列之探索高阶函数中inline修饰符(三)
  • [译]Effective Kotlin系列之遇到多个构造器参数要考虑使用构建器(二)
  • [译]Effective Kotlin系列之考虑使用静态工厂方法替代构造器(一)

实战系列:

  • 用Kotlin撸一个图片压缩插件ImageSlimming-导学篇(一)
  • 用Kotlin撸一个图片压缩插件-插件基础篇(二)
  • 用Kotlin撸一个图片压缩插件-实战篇(三)
  • 浅谈Kotlin实战篇之自定义View图片圆角简单应用

你可能感兴趣的:(Kotlin)