Java - 方法的参数声明
给方法的参数加上限制是很常见的,比如参数代表索引时不能为负数、对于某个关键对象引用不能为null,否则会进行一些处理,比如抛出相应的异常信息。
对于这些参数限制,方法的提供者必须在文档中注明,并且在方法开头时检查参数,并在失败时提供明确的信息,即:
detect errors as soon as possible after they occur
这将成为准确定位错误的一大保障。
如果没有做到这一点,最好的情况是方法在处理过程中失败并抛出了莫名其妙的异常,错误的源头变得难以定位,但这是最好的情况。
更差的情况是方法执行通过,没有发生任何错误,只是得出的结果和方法描述完全不符,最后在某个关键的部分看到奇怪的数据时才亡羊补牢。
对于参数违反有效性时使用的异常类,我们通常抛出IllegalArgumentException,IndexOutOfBoundsException,NullPointerException。
而对于违反约束时抛出的异常类型,需要用Javadoc的@throws标签对其进行说明。
另外,并不是所有参数检查都需要做到这种地步。
如果方法或构造器不对外导出,则可以简单使用assert来保证参数的有效性。
比如java.util.Collections$CopiesList:
private static class CopiesList<E>
extends AbstractList<E>
implements RandomAccess, Serializable{
//...
CopiesList(int n, E e) {
assert n >= 0;
this.n = n;
element = e;
}
//...
}
但是,有效性检查并不都是简单的,这一操作的代价也可能非常大甚至不切实际,于是有些操作直接将参数检查隐含在计算过程中。
比如java.util.Collections的sort()方法,列表中的元素当然是都可比较的,而方法并没有在开头检查有效性,而是计算中遇到问题时抛出异常。
与这种方式相比,提前检查有效性的意义确实不大。
但在计算途中检查参数的有效性需要考虑一点,即方法的原子性。
说道参数就不得不说方法重载,首先上一段例子:
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class CollectionClassifier {
public static String classify(Set<?> s) {
return "Set";
}
public static String classify(List<?> lst) {
return "List";
}
public static String classify(Collection<?> c) {
return "Unknown Collection";
}
public static void main(String[] args) {
Collection<?>[] collections = { new HashSet<String>(),
new ArrayList<BigInteger>(),
new HashMap<String, String>().values() };
for (Collection<?> c : collections)
System.out.println(classify(c));
}
}
很常见的笔试题,会输出三次"Unknown Collection",编译时已决定了将调用的方法。
与重载方法的静态选择(为什么当初会这样设计重载? overriding is the norm and overloading is the exception,似乎没有人希望这种做法 )相对的是覆盖方法,overridden method的选择是动态的。
即,选择方法是在运行时进行的,子类用同样的方法签名覆盖了上级类的方法,如果这个方法是实例方法则会在子类的实例上被调用。
比如下面这个例子,实例的编译时类型对其没有造成影响:
class Wine {
String name() {
return "wine";
}
}
class SparklingWine extends Wine {
@Override
String name() {
return "sparkling wine";
}
}
class Champagne extends SparklingWine {
@Override
String name() {
return "champagne";
}
}
public class Overriding {
public static void main(String[] args) {
Wine[] wines = { new Wine(), new SparklingWine(), new Champagne() };
for (Wine wine : wines)
System.out.println(wine.name());
}
}
鉴于重载方法的这种特征,而语言本身对其也没有特别的限制,作者建议<不要提供相同参数数量的重载方法>,而对于可变参数则不要考虑重载。
关于这个建议的不错的例子就是ObjectOutputStream,对于其write方法,设计者并没有提供相同参数数量的重载,而是提供了诸如writeInt,writeBoolean,writeLong等方法,而且read方法也是与write对称的。
这种方式不适用于构造器,我们无法对构造器进行命名,但我们可以对构造器进行私有化并导出静态工厂。
但也并不能说必须严格遵守这种规则,比如为fetchSalaryInfo()提供了两种方法重载,分别是int uid和User userInfo,很难想象会出现调用错误的情况。
破坏<不要提供相同参数数量的重载方法>这一规则不仅仅是出现在导出某个方法的时候,更新现有类的时候也有可能。
比如String类有一个since 1.4的contentEquals(StringBuffer),另外还有一个since 1.5的contentEquals(CharSequence)。
(Java 1.5版本中增加了CharSequence接口,并作为StringBuffer,StringBuilder,CharBuffer,String等类的公共接口)
但这并不会带来危害,因为这个例子的两个重载方法的行为是完全一样的。
对于泛型还有下面这种有趣的情况:
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
public class SetList {
public static void main(String[] args) {
Set<Integer> set = new TreeSet<Integer>();
List<Integer> list = new ArrayList<Integer>();
for (int i = -3; i < 3; i++) {
set.add(i);
list.add(i);
}
for (int i = 0; i < 3; i++) {
set.remove(i);
list.remove(i);
}
System.out.println(set + " " + list);
}
}
槽点:remove里的参数是index还是element?
执行结果是[-3,-2,-1][-2,0,2],也就是说 list.remove中的参数是index,而不是被自动装箱。
应该说问题根本原因是List同时提供了remove(int)和remove(E)吗?
但作为API的使用者,我们能做的仅仅是对Java 5的这一大特性持更谨慎的态度。
Java - 方法的参数声明的更多相关文章
- Java方法的参数传递方式为: 值传递
Java方法的参数传递方式为: 值传递 对于基本数据类型作为参数传递时, 是"按值传递", 这点都认识很清楚. 但是, 当对象或者说引用作为参数传递, Java 的参数传递方式是& ...
- Java方法传递参数传值还是传址的问题
这几天重构项目代码遇到一个疑问:可不可以在方法A中定义一个boolean变量b为false,然后A调用方法C把b传递到C方法中经过一些列业务判断后修改为true,C执行结束后A方法中b的值还是原来的f ...
- Java方法的参数是按值传递的.【转】
在Java中,所有的方法参数,都是"按值传递". 有那么一种说法,Java中基本类型是按值传递,对象是按引用传递.这个说法其实是不确切的,确切的说法是 Java中基本类型将值作为参 ...
- 关于Java方法的参数
刚好看到C++的函数这块,说C++中除了引用类型的形参,其他都是实参的副本(个人总结). 隐约记得Java中方法的参数也是这么回事,于是手动测试一番. 结果 Java中方法的参数都是值传递,哪怕是引用 ...
- JAVA方法中参数到底是值传递还是引用传递
当一个对象被当作参数传递到一个方法后,在此方法内可以改变这个对象的属性,那么这里到底是值传递还是引用传递? 答:是值传递.Java 语言的参数传递只有值传递.当一个实例对象作为参数被传递到方法中时,参 ...
- java方法可变参数研究
1 问题引出 (1)缘由 最近在研究如何在项目中引入Redis缓存,于是遇到可变参数这个疑惑点,之前没有好好研究过,为了避免项目后期出现问题. (2)项目相关技术 SpringBoot Redis K ...
- java方法---可变参数
可变参数 在方法的声明中,在指定参数类型后面加一个...(省略号) 一个方法中只能指定一个可变参数,它必须是方法的最后一个参数,任何普通参数必须在它之前声明:
- java方法可变参数的写法
jdk1.5之后出现的,该写法避免了当有多个不同个数的参数方法时,对方法的重载.其实就是数组. package com.shipin; /** * @author QiaoJiafei * @vers ...
- JAVA方法传递参数:传值?传引用?
先来看下面这三段代码: //Example1: public class Example1 { static void check(int a) { a++; } public static void ...
随机推荐
- Python3.5 学习三
对文件的操作 打开模式: 1 f=open("xxx","r",encoding=="utf-8") 只读 2 f=open("x ...
- JAVA常见面试题之Forward和Redirect的区别 (转发和重定向)
阅读目录 一:间接请求转发(Redirect) 二:直接请求转发(Forward) 用户向服务器发送了一次HTTP请求,该请求可能会经过多个信息资源处理以后才返回给用户,各个信息资源使用请求转发机制相 ...
- bzoj 2244 [SDOI2011]拦截导弹(dp+CDQ+树状数组)
传送门 题解 看了半天完全没发现这东西和CDQ有什么关系…… 先把原序列翻转,求起来方便 然后把每一个位置表示成$(a,b,c)$其中$a$表示位置,$b$表示高度,$c$表示速度,求有多少个位置$a ...
- 在 django模型中封装元组和字典, 字段中使用chioce参数实现数据的一一对应
一.models.py中 class OrderInfo(BaseModel): '''订单模型类''' # 封装一个字典, 便于在视图中取值, 进行比对 PAY_METHODS = { : &quo ...
- Jsp入门实战上
前面讲了servlet入门实践现在开始介绍jsp入门实践,开发环境的搭建请参考我前面的tomcat的文章,jsp入门教程分为上下两部分,第一部分简单讲解:jsp语法的规范,以及三大编译指令,七个动作指 ...
- SAE实践——用SVN命令行同步/提交代码
1. 同步应用到本地 注:首次使用svn需要输入安全认证密码 在终端输入以下命令 svn co https://svn.sinaapp.com/nyhello nyhello替换为自己的应用名称. 用 ...
- eclipse如何设置时常挂提示
(.qwertyuiopasdfghjklzxcvbnm)
- 学习GO第一天,自我感觉可麻利的开干了-GO语言配置、开发、服务器部署
学习GO第一天,自我感觉可麻利的开干了-GO语言配置.开发.服务器部署 第一步下载 go sdk https://golang.org/dl/ https://storage.googleapis.c ...
- [DPF] DPF 常见的问题
问题一: SQL6031N Error in the db2nodes.cfg file at line number "5". Reason code "12&qu ...
- (转)MySQL高可用解决方案
MySQL高可用解决方案 原文:http://www.ywnds.com/?p=5565 有这么两个概念,数据库的可靠性和数据库的可用性,可靠性指的是数据可靠,而可用性指的是服务可用.但是不管是可靠性 ...