Varargs(可变参数类型)详解

java Varargs详解

   在java实际开发中,很多时候我们无法确定一个函数的参数个数。以一个能实现求和的函数为例,事先你不知道有多少个数会被相加,你甚至不会知道他们的类型是什么,但是,Java要求实参(Arguments)和形参(Parameters)的数量和类型都必须逐一匹配。即使是通过重载机制,为同一个方法提供带有不同数量的形参的版本,仍然不能达到让实参数量任意变化的目的。在J2SE 1.4为止,传统的方法是通过传递一个数组,利用数组来包裹要传递的实参来满足这个需求。

static int  Variable_Element(int[] args){
		int sum = 0;
		for(int s:args)
			   sum=sum+s;;
			   return sum;
	}
	public static void main(String[] args){
		
		System.out.println(Variable_Element (new int[]{1,2,3}));
	}

   这种做法可以有效的达到“让方法可以接受个数可变的参数”的目的,只是调用时的形式不够简单。

  J2SE 1.5中提供了Varargs机制,Varargs”是“variable number of arguments”的意思允许直接定义能和不定个实参相匹配的形参。从而,可以用一种更简单的方式,来传递个数可变的实参。只要在参数定义的括号里写上一个形参的“类型”与“参数名”之间加上三个连续的“.”(即“...”,英文里的句中省略号),就可以让它和不确定个实参相匹配。而一个带有这样的形参的方法,就是一个实参个数可变的方法。

   那么问题来了,如果这个函数的参数列表是已知的固定参数与可变参数的混合,那么如果继续使用单一的可变参数就会造成浪费。好在Varargs机制并不排斥其他固定实参,只是有两点需要注意。一是只有最后一个形参才能被定义成“能和不确定个实参相匹配”的可变形参,如果这个方法还有其它的形参,要把它们放到前面的位置上由此造成了第二点:一个方法里只能有一个这样的可变形参下面两种写法均会报错:

*
	 * 只有最后一个形参才能被定义成“能和不确定个实参相匹配”的可变形参,
	 * 如果这个方法还有其它的形参,要把它们放到前面的位置上。
	 * 会报The variable argument type int of the method Variable_Element must be the last parameter
	 */
	static int Variable_Element(int...args,String str){
 		int sum = 0;
		for(int s:args)
			   sum=sum+s;;
			   return sum;
	}
        //一个方法里只能有一个这样的可变形参
	static int Variable_Element(int...args,String...str){
 		int sum = 0;
		for(int s:args)
			   sum=sum+s;;
			   return sum;
	}

    形参只能放在最后一位的原因是编译器会在背地里把这最后一个形参转化为一个数组形参,并在编译出的class文件里作上一个记号,表明这是个实参个数可变的方法。因为编译器会进行数组转化,因此我们无法重新定义一个和数组转化后的形参不定类一样的类,例如下面的定义就会报错:

        //会报重复命名的错误,说明Varargs是数组包裹的一种实现
	Variable_Element(int[] args){
	}	
	 Variable_Element(int...str){
	}

    但是和数组不一样的是,你不再需要显式地编写数组语法了,你只需要把参数写在对应的位置,当你指定参数时,编译器实际上会为你去填充数组,所以你获得是仍旧是一个数组。但是同样,如果你传进去的参数是一个数组,那么编译器会发现他是一个数组,所以不会执行任何转换,例如:

       Variable_Element(1,2,3,4);
       Variable_Element(new int[]{1,2,3,4});

   这两种写法都是可以的,但是,这并不表示“能匹配不确定个实参的形参”和“数组形参”完全没有差异。比如:如果在一个定义为数组形参的函数中如果直接传入1234是会报cannot be applied to的编译错误。

下面是Varargs机制中最令我感兴趣的发现,在我查阅的其他资料(java 1.5)中明确提出Varargs无法和泛型配合传递不定性不定量形参,但是在我的测试中,java1.7已经支持了这种定义方式,这样,我们文章开头所提出的问题也就有了解决方案:如何实现一个不限数字类型和数字个数的加法。代码如下:

static  double  Variable_Element(T...args){
       double sum=0;
       for (T t : args) {
		sum=Double.parseDouble(t.toString())+sum;
	}
	return sum;
	}
	public static void main(String[] args){
		System.out.println(Variable_Element(1,2,3,6.6));
	}

   Java支持“重载”的机制,允许在同一个类拥有许多只有形参列表不同的方法。然后,由编译器根据调用时的实参来选择到底要执行哪一个方法。

   传统上的选择,基本是依照“特殊者优先”的原则来进行。一个方法的特殊程度,取决于为了让它顺利运行而需要满足的条件的数目,需要条件越多的越特殊。

    在引入Varargs机制之后,这一原则仍然适用,只是要考虑的问题丰富了一些——传统上,一个重载方法的各个版本之中,只有形参数量与实参数量正好一致的那些有被进一步考虑的资格。但是Varargs机制引入之后,完全可以出现两个版本都能匹配,在其它方面也别无二致,只是一个实参个数固定,而一个实参个数可变的情况。

   遇到这种情况时,所用的判定规则是“实参个数固定的版本优先于实参个数可变的版本”。比如说给其中一个方法添加一个非可变参数就可以解决这个问题。注意,此时固定的实参类型不能和可变的形参类型相同,否则编译器会报错。

static void Variable_Element(String...args){
    System.out.println("String...args");
	}
	static void  Variable_Element(String str,String...args){
	    System.out.println("String str");
		}
	public static void main(String[] args){
		Variable_Element("哈哈");
	}

    这段代码就报错了,有人可能会疑惑,第一个方法是一个参数,第二个方法是两个参数,我调用时仅传入一个参数,怎么会报错呢?这是因为Varargs机制中允许可变形参数目为0,但这并不是数组中的空数组,它并不是null;

   和“用数组包裹”的做法相比,真正的实参个数可变的方法,在调用时传递参数的操作更为简单,含义也更为清楚。尤其是和泛型的配合,让它能给我们在某些特定情况下解决问题。









你可能感兴趣的:(java)