问:下面程序段能正常编译吗?输出是什么?为什么?
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 变长参数语法糖为了兼容数组类型的一种无法避免的遗留问题吧。因为标准的方法重写必须满足如下条件:
-
重写方法不能缩小访问权限,可扩大;
-
形参列表必须与被重写方法相同;
-
返回类型必须与被重写方法的相同或是其子类型;
-
重写方法不能抛出新异常或者超过父类方法范围的异常,可以抛出更少或者不抛出异常;