- 浏览: 426738 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
burningblood:
最近也遇到了这个细节问题。我用的是4,里面没有 get.rel ...
httpclient的并发连接问题 -
greatwqs:
使用HttpURLConnection注意设置超时 -
qinweilh:
...
tomcat报错:standardServer.await: create[8005]: -
jayyunfei:
还是不很明白
JPA entityManager的管理 -
a418040445:
...
Calendar
J2SE
1.5提供了“Varargs”机制。借助这一机制,可以定义能和多个实参相匹配的形参。从而,可以用一种更简单的方式,来传递个数可变的实参。本文介绍
这一机制的使用方法,以及这一机制与数组、泛型、重载之间的相互作用时的若干问题。
到J2SE
1.4为止,一直无法在Java程序里定义实参个数可变的方法——因为Java要求实参(Arguments)和形参(Parameters)的数量和类
型都必须逐一匹配,而形参的数目是在定义方法时就已经固定下来了。尽管可以通过重载机制,为同一个方法提供带有不同数量的形参的版本,但是这仍然不能达到
让实参数量任意变化的目的。
然而,有些方法的语义要求它们必须能接受个数可变的实参——例如著名的main方法,就需要能接受所有的
命令行参数为实参,而命令行参数的数目,事先根本无法确定下来。
对于这个问题,传统上一般是采用“利用一个数组来包裹要传递的实参”的
做法来应付。
1. 用数组包裹实参
“用数组包裹实参”的做法可以分成三步:首先,为这个方法定义一个数组型的参数;然后在调
用时,生成一个包含了所有要传递的实参的数组;最后,把这个数组作为一个实参传递过去。
这种做法可以有效的达到“让方法可以接受个数可
变的参数”的目的,只是调用时的形式不够简单。
J2SE
1.5中提供了Varargs机制,允许直接定义能和多个实参相匹配的形参。从而,可以用一种更简单的方式,来传递个数可变的实参。
Varargs
的含义
大体说来,“Varargs”是“variable number of
arguments”的意思。有时候也被简单的称为“variable
arguments”,不过因为这一种叫法没有说明是什么东西可变,所以意义稍微有点模糊。
2. 定义实参个数可变的方法
只
要在一个形参的“类型”与“参数名”之间加上三个连续的“.”(即“...”,英文里的句中省略号),就可以让它和不确定个实参相匹配。而一个带有这样的
形参的方法,就是一个实参个数可变的方法。
清单1:一个实参个数可变的方法
private static int sumUp(int... values) { }
注意,只有最后一个形参才能被定义成“能和不确定个实参相匹配”的。因此,一个方法里只能有一个这样的形参。另外,如果这个方法还有其它的形参,要
把它们放到前面的位置上。
编译器会在背地里把这最后一个形参转化为一个数组形参,并在编译出的class文件里作上一个记号,表明这是
个实参个数可变的方法。
清单2:实参个数可变的方法的秘密形态
private static int sumUp(int[] values) { }
由于存在着这样的转化,所以不能再为这个类定义一个和转化后的方法签名一致的方法。
清单3:会导致编译错误的组合
private static int sumUp(int... values) { } private static int sumUp(int[] values) { }
空白的存亡问题
根据J2SE
1.5的语法,在“...”前面的空白字符是可有可无的。这样就有在“...”前面添加空白字符(形如“Object ...
args”)和在“...”前面不加空白字符(形如“Object... args”)的两种写法。因为目前和J2SE 1.5相配合的Java
Code Conventions还没有正式发布,所以无法知道究竟哪一种写法比较正统。不过,考虑到数组参数也有“Object []
args”和“Object[] args”两种书写方式,而正统的写法是不在“[]”前添加空白字符,似乎采取不加空白的“Object...
args”的写法在整体上更协调一些。
3. 调用实参个数可变的方法
只要把要传递的实参逐一写到相应的位置上,就可以调用一
个实参个数可变的方法。不需要其它的步骤。
清单4:可以传递若干个实参
sumUp(1, 3, 5, 7);
在背
地里,编译器会把这种调用过程转化为用“数组包裹实参”的形式:
清单5:偷偷出现的数组创建
sumUp(new
int[]{1, 2, 3, 4});
另外,这里说的“不确定个”也包括零个,所以这样的调用也是合乎情理的:
清单6:也
可以传递零个实参
sumUp();
这种调用方法被编译器秘密转化之后的效果,则等同于这样:
清单7:零实参对应空
数组
sumUp(new int[]{});
注意这时传递过去的是一个空数组,而不是null。这样就可以采取统一的形式来处理,而
不必检测到底属于哪种情况。
4. 处理个数可变的实参
处理个数可变的实参的办法,和处理数组实参的办法基本相同。所有的实
参,都被保存到一个和形参同名的数组里。根据实际的需要,把这个数组里的元素读出之后,要蒸要煮,就可以随意了。
清单8:处理收到的实
参们
- private static int sumUp( int ... values) {
- int sum = 0 ;
- for ( int i = 0 ; i < values.length; i++) {
- sum += values[i];
- }
- return sum;
- }
private static int sumUp(int... values) { int sum = 0; for (int i = 0; i < values.length; i++) { sum += values[i]; } return sum; }
5. 转发个数可变的实参
有时候,在接受了一组个数可变的实参之后,还要把它们传递给另一个实参个数可变的方法。因为编码时无法
知道接受来的这一组实参的数目,所以“把它们
逐一写到该出现的位置上去”的做法并不可行。不过,这并不意味着这是个不可完成的任务,因为还有另外一种办法,可以用来调用实参个数可变的方法。
在J2SE
1.5的编译器的眼中,实参个数可变的方法是最后带了一个数组形参的方法的特例。因此,事先把整组要传递的实参放到一个数组里,然后把这个数组作为最后一
个实参,传递给一个实参个数可变的方法,不会造成任何错误。借助这一特性,就可以顺利的完成转发了。
清单9:转发收到的实参们
- public class PrintfSample {
- public static void main(String[] args) {
- // 打印出“Pi:3.141593 E:2.718282”
- printOut( "Pi:%f E:%f\n" , Math.PI, Math.E);
- }
- private static void printOut(String format, Object... args) {
- // J2SE 1.5里PrintStream新增的 printf(String format, Object... args)方法
- System.out.printf(format, args);
- }
- }
public class PrintfSample { public static void main(String[] args) { // 打印出“Pi:3.141593 E:2.718282” printOut("Pi:%f E:%f\n", Math.PI, Math.E); } private static void printOut(String format, Object... args) { // J2SE 1.5里PrintStream新增的printf(String format, Object... args)方法 System.out.printf(format, args); } }
Java里的“printf”和“sprintf”
C语言里的printf(按一定的格式输出字符串)和sprintf(按一定
的格式组合字符串)是十分经典的使用Varargs机制的例子。在 J2SE
1.5中,也分别在java.io.PrintStream类和java.lang.String类中提供了类似的功能。
按一定的格式
输出字符串的功能,可以通过调用PrintStream对象的printf(String format, Object... args)方法来实现。
按一定的格式组合字符串的工作,则可以通过调用String类的String format(String format,
Object... args)静态方法来进行。
6. 是数组?不是数组?
尽管在背地里,编译器会把能匹配不确定个实参的形
参,转化为数组形参;而且也可以用数组包了实参,再传递给实参个数可变的方法;但是,这并不表示“能匹配不确定个实参的形参”和“数组形参”完全没有差
异。
一个明显的差异是,如果按照调用实参个数可变的方法的形式,来调用一个最后一个形参是数组形参的方法,只会导致一个“cannot
be applied to”的编译错误。
清单10:一个“cannot be applied to”的编译错误
- private static void testOverloading( int [] i) {
- System.out.println( "A" );
- }
- public static void main(String[] args) {
- testOverloading( 1 , 2 , 3 ); // 编译出错
- }
private static void testOverloading(int[] i) { System.out.println("A"); } public static void main(String[] args) { testOverloading(1, 2, 3);// 编译出错 }
由于这一原因,不能在调用只支持用数组包裹实参的方法的时候(例如在不是专门为J2SE
1.5设计第三方类库中遗留的那些),直接采用这种简明的调用方式。
如果不能修改原来的类,为要调用的方法增加参数个数可变的版本,而
又想采用这种简明的调用方式,那么可以借助“引入外加函数(Introduce Foreign Method)”和“引入本地扩展(Intoduce
Local Extension)”的重构手法来近似的达到目的。
7. 当个数可变的实参遇到泛型
J2SE
1.5中新增了“泛型”的机制,可以在一定条件下把一个类型参数化。例如,可以在编写一个类的时候,把一个方法的形参的类型用一个标识符(如T)来代表,
至于这个标识符到底表示什么类型,则在生成这个类的实例的时候再行指定。这一机制可以用来提供更充分的代码重用和更严格的编译时类型检查。
不
过泛型机制却不能和个数可变的形参配合使用。如果把一个能和不确定个实参相匹配的形参的类型,用一个标识符来代表,那么编译器会给出一个“generic
array creation”的错误。
清单11:当Varargs遇上泛型
private static <T> void testVarargs(T... args) { // 编译出错 }
造成这个现象的原因在于J2SE
1.5中的泛型机制的一个内在约束——不能拿用标识符来代表的类型来创建这一类型的实例。在出现支持没有了这个约束的Java版本之前,对于这个问题,基
本没有太好的解决办法。
不过,传统的“用数组包裹”的做法,并不受这个约束的限制。
清单12:可以编译的变通做法
- private static <T> void testVarargs(T[] args) {
- for ( int i = 0 ; i < args.length; i++) {
- System.out.println(args[i]);
- }
- }
private static <T> void testVarargs(T[] args) { for (int i = 0; i < args.length; i++) { System.out.println(args[i]); } }
8. 重载中的选择问题
Java支持“重载”的机制,允许在同一个类拥有许多只有形参列表不同的方法。然后,由编译器根据调用时
的实参来选择到底要执行哪一个方法。
传统上的选择,基本是依照“特殊者优先”的原则来进行。一个方法的特殊程度,取决于为了让它顺利运
行而需要满足的条件的数目,需要条件越多的越特殊。
在引入Varargs机制之后,这一原则仍然适用,只是要考虑的问题丰富了一些——
传统上,一个重载方法的各个版本之中,只有形参数量与实参数量正
好一致的那些有被进一步考虑的资格。但是Varargs机制引入之后,完全可以出现两个版本都能匹配,在其它方面也别无二致,只是一个实参个数固定,而一
个实参个数可变的情况。
遇到这种情况时,所用的判定规则是“实参个数固定的版本优先于实参个数可变的版本”。
清单
13:实参个数固定的版本优先
- public class OverloadingSampleA {
- public static void main(String[] args) {
- testOverloading( 1 ); // 打印出A
- testOverloading( 1 , 2 ); // 打印出B
- testOverloading( 1 , 2 , 3 ); // 打印出C
- }
- private static void testOverloading( int i) {
- System.out.println( "A" );
- }
- private static void testOverloading( int i, int j) {
- System.out.println( "B" );
- }
- private static void testOverloading( int i, int ... more) {
- System.out.println( "C" );
- }
- }
public class OverloadingSampleA { public static void main(String[] args) { testOverloading(1);// 打印出A testOverloading(1, 2);// 打印出B testOverloading(1, 2, 3);// 打印出C } private static void testOverloading(int i) { System.out.println("A"); } private static void testOverloading(int i, int j) { System.out.println("B"); } private static void testOverloading(int i, int... more) { System.out.println("C"); } }
如果在编译器看来,同时有多个方法具有相同的优先权,它就会陷入无法就到底调用哪个方法作出一个选择的状态。在这样的时候,它就会产生一个
“reference to 被调用的方法名 is ambiguous”的编译错误,并耐心的等候作了一些修改,足以免除它的迷惑的新源代码的到来。
在引入了Varargs机制之后,这种可能导致迷惑的情况,又增加了一些。例如现在可能会有两个版本都能匹配,在其它方面也如出一辙,
而且都是实参个数可变的冲突发生。
清单14:左右都不是,为难了编译器
- public class OverloadingSampleB {
- public static void main(String[] args) {
- testOverloading( 1 , 2 , 3 ); // 编译出错
- }
- private static void testOverloading(Object... args) {
- }
- private static void testOverloading(Object o, Object... args) {
- }
- }
public class OverloadingSampleB { public static void main(String[] args) { testOverloading(1, 2, 3);// 编译出错 } private static void testOverloading(Object... args) { } private static void testOverloading(Object o, Object... args) { } }
另外,因为J2SE
1.5中有“Autoboxing/Auto-Unboxing”机制的存在,所以还可能发生两个版本都能匹配,而且都是实参个数可变,其它方面也一模一
样,只是一个能接受的实参是基本类型,而另一个能接受的实参是包裹类的冲突发生。
清单15:Autoboxing/Auto-
Unboxing带来的新问题
- public class OverloadingSampleC {
- public static void main(String[] args) { /* 编译出错 */
- testOverloading( 1 , 2 ); /* 还是编译出错 */
- testOverloading( new Integer( 1 ), new Integer( 2 ));
- }
- private static void testOverloading( int ... args) {
- }
- private static void testOverloading(Integer... args) {
- }
- }
public class OverloadingSampleC { public static void main(String[] args) { /* 编译出错 */ testOverloading(1, 2); /* 还是编译出错 */ testOverloading(new Integer(1), new Integer(2)); } private static void testOverloading(int... args) { } private static void testOverloading(Integer... args) { } }
9. 归纳总结
和“用数组包裹”的做法相比,真正的实参个数可变的方法,在调用时传递参数的操作更为简单,含义也更为清楚。不
过,这一机制也有它自身的局限,并不是一个完美无缺的解决方案。
发表评论
-
糟糕透顶的axis2
2015-02-06 15:27 861我记得很早以前在网上看过文章,大致讲axis2的质量如何 ... -
使用java连接https的问题
2012-03-19 15:41 858在使用hudson的过程中,我们需要发送邮件的功能。但是公司的 ... -
SSL exception: "No subject alternative names matching IP address ..." & "No name
2012-02-08 09:35 10752When you want to establish an S ... -
ResourceBundle加载文件的顺序
2011-12-24 15:01 1366If a ResourceBundle class for ... -
异常处理框架
2011-09-07 14:57 825The Nature of Exceptions Bro ... -
httpclient的并发连接问题
2011-05-24 16:14 6687昨天的搜索系统又出状况了,几个库同时重建索引变得死慢。经 ... -
java connect https
2011-05-04 15:37 1020When I use java to connect HTTP ... -
jvm的高性能
2011-04-25 13:48 808jdk将源代码编译成字节码之后,由JVM在运行期对其进行解释执 ... -
java annotation
2011-03-10 14:58 907JDK内置的annotaion 1. @Target ... -
java字节码的操纵
2011-03-09 16:35 1533http://www.infoq.com/cn/article ... -
java 范型
2011-03-09 15:15 759Java泛型(generics)是JDK 5中引入的一个新特性 ... -
正确使用 Volatile 变量
2011-03-04 09:34 792Java 语言中的 volatile 变 ... -
java中Thread与Runnable的区别
2011-02-25 20:42 1746在java中可有两种方式实现多线程,一种是继承Thread类, ... -
copy-on-write
2010-12-08 10:29 934Copy-on-write (sometimes refe ... -
synchronized原理
2010-11-29 14:40 1860每个JAVA对象都有一把锁, 当有多个线程同时访问共享资源的时 ... -
Map 四种同步方式的性能比较
2010-11-25 11:50 976如果需要使 Map 线程安全,大致有这么四种方法: 1、 ... -
Java中HashMap,LinkedHashMap,TreeMap的区别
2010-11-25 11:49 1750java为数据结构中的映射 ... -
java的内存泄漏
2010-11-25 10:52 10521 引言 Java的一个重要优点就是通过垃圾收集器 ... -
解析Java对象的equals()和hashCode()的使用
2010-11-25 10:49 1092前言 在Java语言中,equals()和hash ... -
java map
2010-11-25 10:40 11601. Map key: 同一个key必须hashcode相同。 ...
相关推荐
在J2SE 1.4之前,Java中无法定义实参个数可变的方法,因为Java要求实参和形参的数量和类型都必须逐一匹配。然而,有些方法的语义要求它们必须能接受个数可变的实参,例如main方法,就需要能接受所有的命令行参数为...
在Java 1.4之前,无法在Java程序中定义实参数量可变的方法,因为Java要求实参数量和形参数量必须逐一匹配,但是通过使用数组包裹实参数的方法可以达到让方法接受个数可变的参数的目的。 在Java 1.5中,引入了Var...
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于计算机科学与技术等相关专业,更为适合;
白色大气风格的影视传媒公司企业网站源码下载.zip
白色大气风格的电子邮件订阅模板下载.zip
TenonOS:简化复杂性,释放创新力,重塑操作系统的开发模式。TenonOS是一款基于LibOS架构的操作系统,旨在提升操作系统扩展、裁剪、移植效率,基于丰富的微库组件池,实现跨场景、跨行业、跨领域的快速能力复用,灵活生成与场景最契合的OS实体。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于计算机科学与技术等相关专业,更为适合;
白色大气风格的响应式CSS3模板下载.zip
白色大气风格的VPS销售网站模板.zip
白色大气风格的西餐烧烤甜品网站模板下载.zip
白色简洁的服务企业网站模板下载.zip
白色大气风格的医院网站模板下载.zip
白色简洁的文化传媒网页企业模板下载.zip
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于计算机科学与技术等相关专业,更为适合;
谓语v.分类.pdf111111111aaaaaaaa
路由器设备现场配置手册
2023-04-06-项目笔记-第三百五十八阶段-课前小分享_小分享1.坚持提交gitee 小分享2.作业中提交代码 小分享3.写代码注意代码风格 4.3.1变量的使用 4.4变量的作用域与生命周期 4.4.1局部变量的作用域 4.4.2全局变量的作用域 4.4.2.1全局变量的作用域_1 4.4.2.356局变量的作用域_356- 2024-12-25
白色扁平化风格的五谷养生模板下载.zip
内容概要:本文详细介绍了C++模板与泛型编程的基础和进阶知识,包括模板的概念与优势、函数模板和类模板的声明与实例化、模板元编程、SFINAE原则、模板特化与偏特化、类型推导与auto关键字、模板与递归、高级模板技巧、模板在容器设计和算法中的应用、模板与多态、模板与性能优化,以及模板在实际项目中的案例分析。每部分内容都通过具体示例进行说明,使读者能够深入理解并掌握C++模板的各种应用。 适合人群:具备C++基础知识,希望深入了解模板与泛型编程的技术人员。 使用场景及目标:①理解和应用C++模板的基本概念和语法;②掌握模板在泛型编程中的重要性;③学习模板元编程和SFINAE原则;④理解模板特化与偏特化的使用场景;⑤探索模板在STL中的应用;⑥通过实际案例提升模板设计能力。 阅读建议:本文内容丰富且理论与实践结合紧密,建议读者边读边动手编写代码,以便更好地理解模板机制和泛型编程的精髓。
llvm cmake