Java语法糖初探(三)--变长参数

变长参数概念

在Java5 中提供了变长参数(varargs),也就是在方法定义中可以使用个数不确定的参数,对于同一方法可以使用不同个数的参数调用。形如 function(T …args)。但是需要明确的一点是,java方法的变长参数只是语法糖,其本质上还是将变长的实际参数 varargs 包装为一个数组。

看下面的例子:

12345678910111213
public class VariVargs { public static void main(String []args) {   test("hello","welcome","hi"); } public static void  test(String ...args) {   for(String string:args)   {     System.out.println(string);   } }}

将上述代码编译后,再反编译得到的结果如下:

1234567891011121314151617181920
import java.io.PrintStream;

public class VariVargs{  public static void main(String[] args)  {    test(      new String[] { "hello", "welcome", "hi" });  }

public static void test(String[] args) {    String[] arrayOfString = args; //here    int j = args.length;    for (int i = 0; i < j; i++)    {      String string = arrayOfString[i]; //String string = arrayOfString[i];      System.out.println(string);    }  }}

从上面的代码可以看出java的编译器将变长参数转换成了数组,并且显示的使用了arrayOfString指向了args。后面对变长参数的操作都转换为对arrayOfString的操作。(如果变长参数是Integer型的,数组名就是arrayOfInteger。其他类型类似)

变长参数非常容易理解和使用。但是在使用过程中也会遇到一些小的陷阱,但是只要我们理解了变长参数的本质,那么都能够很容易的理解和避免这些陷阱。

变长参数规则

调用方法时能与固定参数函数以及可变参数函数都匹配时。优先调用固定参数方法

在调用方法时能与固定参数函数以及可变参数函数都匹配时,JVM会优先匹配固定参数方法,下面举一个例子:

123456789101112131415
public class VariVargsTest1 {  //变长参数和固定参数同时存在  public static void main(String[] args) {    // TODO Auto-generated method stub    test("hello");  }  public static void test(String...args)  {    System.out.println("调用可变参数函数****");  }  public static void test(String test)  {    System.out.println("调用固定参数函数----");  }}

执行代码,输出:调用固定参数函数—-

调用方法时,两个变长参数函数都匹配时无法通过编译

例子如下:

123456789101112131415
public class VariVargsTest2 {  //调用方法的参数与两个变长参数匹配,无法通过编译  public static void main(String[] args) {    // TODO Auto-generated method stub   test("hello"); //1  }  public static void test(String ...args)  {    System.out.println("变长参数1");  }  public static void test(String string,String...args)  {    System.out.println("变长参数2");  }}

当没有1处代码时,程序是能够编译通过的。但是当添加了1处代码后无法通过编译,给出的错误是:The method test(String[]) is ambiguous for the type VariVargsTest2。编译器不知道选取哪个方法

一个方法只能有一个变长参数,且只能是参数列表的最后一个

由于规定变长参数只能是参数列表的最后一个。那么一个方法就不会有多个变长参数。

1234567891011
public class VariVargs3 {

public static void main(String[] args) {    // TODO Auto-generated method stub

}   public static void test(String...args1,Integer...args2) //1 编译出错   {

}}

在代码1处编译出错:The variable argument type String of the method test must be the last parameter

变长参数的函数重载问题

首先我们回忆一下函数重载的定义和规则

重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型呢?可以相同也可以不同。
每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。

重载规则:

  • 被重载的方法必须改变参数列表
  • 被重载的方法可以改变返回类型
  • 被重载的方法可以改变访问修饰符
  • 被重载的方法可以声明新的或更广的检查异常
  • 方法能够在同一个类中或者在一个子类中被重载

变长参数与数组重载

经过上面的分析,我们知道编译器将变长参数转换为了数组,所以这两个方法的参数列表是一样的,不满足重载的第一条规则,用一个小例子证明一下:

12345678910111213141516
public class VariVargsTest41 {

public static void main(String[] args) {    // TODO Auto-generated method stub

}  //变长参数与数组重载  public static void test(String ...args)  {

}  public static void test(String []args)  {

}}

上述代码无法通过编译:Duplicate method test(String[]) in type VariVargsTest41

可变参数方法与可变参数方法重载

上面已经分析过,这里不再说明

可变参数方法与无参方法的重载

12345678910111213
public class VariVargsTest4 { public static void test(String ...args) {   System.out.println("调用变长参数函数"); } public static void test() {   System.out.println("调用无参函数"); } public static void main(String []args) {   test(); //调用方法是无参的 }

输出:调用无参函数。

可变参数的重写问题

函数重写的定义和规则

重写(Override)是子类对父类的允许访问的方法的实现过程进行重新编写!返回值和形参都不能改变。即外壳不变,核心重写!
重写的好处在于子类可以根据需要,定义特定于自己的行为。也就是说子类能够根据需要实现父类的方法。

重写规则:

  • 参数列表必须完全与被重写的方法相同,否则不能称其为重写而是重载
  • 返回的类型必须一直与被重写的方法的返回类型相同,否则不能称其为重写而是重载
  • 访问修饰符的限制一定要大于被重写方法的访问修饰符(public>protected>default>private)
  • 重写方法一定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常

验证

我们先定义两个类:

1234567
public class Base {  public void test(String string)  {    System.out.println("Base");  }

}
1234567
public class Child extends Base{public void test(String ...args){ System.out.println("Child");}

}

Child类是Base类的子类

先看下面的测试程序:

12345678
public static void main(String []args) {   Base base=new Child();   base.test("one");   Child child=new Child();   child.test("one");   child.test("one","two"); }

上述代码执行的输出结果是:

Base
Base
Child
base变量是Base类型的,当调用的test函数的参数只有一个时,便会调用Base类中的test(String string)方法。如果是base.test(“one”,”two”)这样调用的话,程序无法通过编译:The method test(String) in the type Base is not applicable for the arguments (String, String)

child变量是Child类型的,当调用的test函数的参数只有一个时,因为Child类继承了Base类,Child类继承了Base类中的test(String string)方法。Child有可变参数方法也有继承来的单参数方法,所以虚拟机会不管变长参数方法,而直接调用完全匹配的那个方法,输出“Base”。

总结

    • 变长参数确实方便了我们的编程,在使用变长参数时,我们需要明确的一点是:这货就是一颗语法糖,天啊我们一直是在对数组做操作
    • 我们在使用变长参数时,需要注意变长参数的使用规则
    • 注意变长参数方法的重载和重写问题
时间: 2024-10-24 10:08:09

Java语法糖初探(三)--变长参数的相关文章

java语法糖

语法糖 Java语法糖系列,所以首先讲讲什么是语法糖.语法糖是一种几乎每种语言或多或少都提供过的一些方便程序员开发代码的语法,它只是编译器实现的一些小把戏罢了,编译期间以特定的字节码或者特定的方式对这些语法做一些处理,开发者就可以直接方便地使用了.这些语法糖虽然不会提供实质性的功能改进,但是它们或能提高性能.或能提升语法的严谨性.或能减少编码出错的机会.Java提供给了用户大量的语法糖,比如泛型.自动装箱.自动拆箱.foreach循环.变长参数.内部类.枚举类.断言(assert)等 断言(as

Java语法糖设计

语法糖 Java语法糖系列,所以首先讲讲什么是语法糖.语法糖是一种几乎每种语言或多或少都提供过的一些方便程序员开发代码的语法,它只是编译器实现的一些小把戏罢了,编译期间以特定的字节码或者特定的方式对这些语法做一些处理,开发者就可以直接方便地使用了.这些语法糖虽然不会提供实质性的功能改进,但是它们或能提高性能.或能提升语法的严谨性.或能减少编码出错的机会.Java提供给了用户大量的语法糖,比如泛型.自动装箱.自动拆箱.foreach循环.变长参数.内部类.枚举类.断言(assert)等 断言(as

Java语法糖1:可变长度参数以及foreach循环原理

语法糖 接下来几篇文章要开启一个Java语法糖系列,所以首先讲讲什么是语法糖.语法糖是一种几乎每种语言或多或少都提供过的一些方便程序员开发代码的语法,它只是编译器实现的一些小把戏罢了,编译期间以特定的字节码或者特定的方式对这些语法做一些处理,开发者就可以直接方便地使用了.这些语法糖虽然不会提供实质性的功能改进,但是它们或能提高性能.或能提升语法的严谨性.或能减少编码出错的机会.Java提供给了用户大量的语法糖,比如泛型.自动装箱.自动拆箱.foreach循环.变长参数.内部类.枚举类.断言(as

【Java】变长参数的坑

VarArgs VarArgs简述 只需要明确一点即可,java方法的变长参数只是语法糖,其本质上还是将变长的实际参数 varargs 包装为一个数组. 所以 Object[] objs 与 Object... objs 被看作是相同的签名,在源码级别是不能同时存在的,因此,无法编译通过 VarArgs包装 包装方式(注意,下面的代码不是实际的实现,而是一个比喻说明): 如果实参是唯一且匹配形参varargs要求的数组(就是实参独占形参varargs),那么可以认为你已经替编译器干了这个活,所以

Java语法糖1:可变长度参数

先抄一段定义: 语法糖(Syntactic Sugar),也称糖衣语法,是由英国计算机学家 Peter.J.Landin 发明的一个术语,指在计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用.Java 中最常用的语法糖主要有泛型.变长参数.条件编译.自动拆装箱.内部类等.虚拟机并不支持这些语法,它们在编译阶段就被还原回了简单的基础语法结构,这个过程成为解语法糖. 简而言之就是语法糖就是为了方便编程,但不影响语言本身功能的情况下做的一种语法处理.虽然没有对语言本身有

java 变长参数使用原则

1.java变长参数用...表示,如Print(String... args){  ... }; 2.如果一个调用既匹配一个固定参数方法,又匹配一个变长参数方法,则优先匹配固定参数的方法 3.如果一个调用能匹配两个及以上的变长参数方法,则出现错误--这其实表示方法设计有问题,编译器会提示The method is ambiguous 4.方法只能有一个变长参数,且必须放在参数列表的最后一个java 变长参数使用原则,布布扣,bubuko.com

Java 变长参数Varargs

Varargs (variable arguments)可变长参数是Java 1.5引入的特性. 方法的形参如print(String ... s),实参为任意数目的值. public class VarargsDemo{ public static void print(String ... s){ for(String a: s) System.out.print(a); } public static void main(String[] args) { print("a", &

java变长参数

从java5开始提供了变长参数,可以把变长参数当作数据使用 可变长参数方法的定义 使用...表示可变长参数,例如 print(String... args){ ... } 在具有可变长参数的方法中可以把参数当成数组使用,例如可以循环输出所有的参数值. print(String... args){ for(String temp:args) System.out.println(temp); } 可变长参数的方法的调用 调用的时候可以给出任意多个参数,例如: print("hello")

深入理解java虚拟机(十二) Java 语法糖背后的真相

语法糖(Syntactic Sugar),也叫糖衣语法,是英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语.指的是,在计算机语言中添加某种语法,这些语法糖虽然不会对语言的功能产生任何影响,却能使程序员更方便的使用语言开发程序,同时增强程序代码的可读性,避免出错的机会.但是如果只是大量添加和使用语法糖,却不去了解他,容易产生过度依赖,从而无法看清语法糖的糖衣背后,程序代码的真实面目. 总而言之,语法糖可以看做是编译器实现的一些"小把戏",这些"小