Nusadua为Java提供了方法参数默认值重载的语法糖, 可以用来简化代码. 我们都知道,Java里经常要通过方法重载来实现参数的默认值,而且只能用重载来实现;而Scala是可以在方法签名里直接定义参数默认值的,这个项目就是为了做到这一点(虽然不是很完美)。
嗯,为什么叫"Nusadua"呢?写Java的都知道Lombok
,而Nusadua
的角色跟Lombok
很像。我去年在巴厘岛旅行的时候,住在Nusa Dua海滩边上,当时就有了实现这个语法糖的想法;而Lombok
这个名字其实也是印度尼西亚的一个岛,龙目岛,天气晴朗的时候,在巴厘岛东岸可以直接看到龙目岛(其实是能看到林贾尼火山,在龙目岛的一座火山),所以……就叫"Nusadua"吧。
首先增加依赖, 以Maven为例,在pom.xml
增加:
<dependency>
<groupId>io.github.leibnizhu</groupId>
<artifactId>nusadua</artifactId>
<version>0.0.2</version>
</dependency>
在Scala里, 是这样定义方法参数的默认值的:
def query(userId: Int = -1, keyword:String =""): List[Xxx] = {
//Do something
}
在Java里,对应代码是:
public List<Xxx> query(Integer userId, String keyword) {
//Do something
}
public List<Xxx> query(String keyword) {
return query(-1, keyword);
}
public List<Xxx> query(Integer userId) {
return query(userId, "");
}
public List<Xxx> query() {
return query(-1, "");
}
嗯,很冗余。
但是, 如果用Nusadua
:
@MethodOverload(field = "userId", defaultInt = -1)
@MethodOverload(field = "keyword", defaultString = "")
public List<Xxx> query(Integer userId, String keyword) {
//Do something
}
就可以了。
对于数组类型的默认值可以用defaultXxxArr
:
@MethodOverload(field = "keyword", defaultStringArr = {""})
public List<Xxx> query(Integer userId, String[] keyword) {
//Do something
}
编译后等价于:
public List<Xxx> query(Integer userId, String[] keyword) {
//Do something
}
public List<Xxx> query(Integer userId) {
return query(userId, new String[]{""});
}
但现在对数组类型支持不是很好(主要是装箱类型的数组).
对于默认值为null的参数,可以使用defaultNull=true
:
@MethodOverload(field = "keyword", defaultNull = true)
public List<Xxx> query(Integer userId, String[] keyword) {
//Do something
}
编译后等价于:
public List<Xxx> query(Integer userId, String[] keyword) {
//Do something
}
public List<Xxx> query(Integer userId) {
return query(userId, null);
}
Java规定了注解的成员属性只能是以下之一:
- 基础类型
- String
- 枚举
- 其他注解
- Class
- 以上类型的数组
嗯,没有Object
,也就是说不能用一个成员属性接收所有类型的方法参数默认值,而且,null也是不允许的。所以才对基础类型和String、以及他们的数组类型定义了这么多注解参数,以及专门为null设置了一个参数。是有点麻烦,不过终归比写一堆重载方法好。
- defaultBool
- defaultBoolArr
- defaultByte
- defaultByteArr
- defaultChar
- defaultCharArr
- defaultDouble
- defaultDoubleArr
- defaultFloat
- defaultFloatArr
- defaultInt
- defaultIntArr
- defaultLong
- defaultLongArr
- defaultNull
- defaultShort
- defaultShortArr
- defaultString
- defaultStringArr
当你在一个方法上使用两个或以上的@MethodOverload
注解的时候,就可能在方法重载的时候会出现方法签名冲突的情况,比如:
@MethodOverload(field = "userName", defaultString = "1")
@MethodOverload(field = "keyword", defaultString = "2")
public List<Xxx> query(String userName, String keyword) {
//Do something
}
Nusadua
首先会生成只有一个参数默认值的方法,如果这个阶段出现了方法签名冲突,会直接报编译异常,比如以上代码在maven编译的时候会报:
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:testCompile (default-testCompile) on project nusadua: Compilation failure
[ERROR] io.github.leibnizhu.nusadua.NusaduaTest.query(String userName,String keyword) method has a MethodOverload annotation's ERROR,Annotation definition: method with same signature (String userName) already existed! Can not continue!
单默认参数方法的生成结束后,会逐一生成两个默认参数的方法、三个默认参数的方法(直到所有默认参数都用上的方法,N个注解理想情况下一共有C(N,1)+C(N,2)+....+C(N,N)
个重载方法)。这个阶段如果出现方法签名冲突,会直接忽略,但哪个方法会被忽略就不受控了,对于这种情况,要尽量避免。比如:
@MethodOverload(field = "str1", defaultString = "true")
@MethodOverload(field = "i", defaultInt = -1)
@MethodOverload(field = "str2", defaultString = "false")
public void multiSignConflict(String str1, int i, String str2) {
System.out.println(String.format("String1=%s, int=%s, String2=%s", str1, i, str2));
}
可能会编译为:
public void multiSignConflict(String str1, int i, String str2) {
System.out.println(String.format("String1=%s, int=%s, String2=%s", str1, i, str2));
}
public void multiSignConflict(int i, String str2) {
this.multiSignConflict("true", i, str2);
}
public void multiSignConflict(String str1, int i) {
this.multiSignConflict(str1, i, "false");
}
public void multiSignConflict(String str1, String str2) {
this.multiSignConflict(str1, -1, str2);
}
public void multiSignConflict(int i) {
this.multiSignConflict("true", i, "false");
}
/**
* 这里是使用了两个默认值的方法,有两个方法签名都是只有一个String,哪个会被保留是不可预估的(实际上可以)
*/
public void multiSignConflict(String str2) {
this.multiSignConflict("true", -1, str2);
}
public void multiSignConflict() {
this.multiSignConflict("true", -1, "false");
}
- 增强完善数组类型的支持。
- 写IntelliJ IDEA的插件(否则IDEA编辑时会不识别新的重载方法,虽然最后编译运行的时候不会出错)