问:下面程序段能正常编译吗?输出是什么?为什么?
public class Demo {
public static void func(String... args) {
System.out.println("func args.");
}
public static void func(String var) {
System.out.println("func val.");
}
public static void main(String[] args) {
func("");
func("", "");
func(null, null);
func(null);
}
}
答:上面程序段 func(null) 无法编译,其他三个调用输出结果如下。
func val.
func args.
func args.
因为在调用方法的时候,如果能和固定参数方法匹配且同时也能与可变长参数方法匹配,则会选择调用固定参数的方法,所以才有了上面的打印。而对于 func(null) 来说,编译器不知道要调用哪个方法,所以要避免 null 值的影响。
问:下面的程序段有什么问题吗?
class Base {
void print(String... args) {
System.out.println("Base print.");
}
}
class Sub extends Base {
@Override
void print(String[] args) {
System.out.println("Sub print.");
}
}
public class Demo {
public static void main(String[] args) {
Base base = new Sub();
base.print(new String[]{""});
base.print("");
Sub sub = new Sub();
sub.print(new String[]{""});
sub.print("");
}
}
答:上面程序段 sub.print("") 无法编译通过,其他没有问题,具体按行执行结果如下。
Sub print.
Sub print.
Sub print.
//sub.print("");编译报错
print(java.lang.String[]) in Sub cannot be applied to (java.lang.String)
首先 base 对象把子类对象 sub 做了向上转型,而形参列表在静态编译时是由父类决定的,所以父类形参列表是标准的变长参数形式,故而调用时能接收字符串或者字符串数组类型,而在动态运行时由于多态特性会调用子类 sub 的重写方法,所以打印两个 Sub print。
而对于 sub 子类直接调用的情况,编译器看到子类覆写了父类的 print 方法,所以形参列表在静态编译时是子类决定的,故而要求必须是 String[] 数组类型,所以打印一个 Sub print 和一个提示参数类型不匹配的编译静态错误。
这是个重写方法的特例,虽然编译器允许这种特例,但是我们实际场景中最好还是不要编写这样的代码,仅当理解成 Java 变长参数语法糖为了兼容数组类型的一种无法避免的遗留问题吧。因为标准的方法重写必须满足如下条件:
重写方法不能缩小访问权限,可扩大;
形参列表必须与被重写方法相同;
返回类型必须与被重写方法的相同或是其子类型;
重写方法不能抛出新异常或者超过父类方法范围的异常,可以抛出更少或者不抛出异常;