给方法的参数加上限制是很常见的,比如参数代表索引时不能为负数、对于某个关键对象引用不能为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.Collectionssort()方法,列表中的元素当然是都可比较的,而方法并没有在开头检查有效性,而是计算中遇到问题时抛出异常。
与这种方式相比,提前检查有效性的意义确实不大。
但在计算途中检查参数的有效性需要考虑一点,即方法的原子性。

说道参数就不得不说方法重载,首先上一段例子:

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 - 方法的参数声明的更多相关文章

  1. Java方法的参数传递方式为: 值传递

    Java方法的参数传递方式为: 值传递 对于基本数据类型作为参数传递时, 是"按值传递", 这点都认识很清楚. 但是, 当对象或者说引用作为参数传递, Java 的参数传递方式是& ...

  2. Java方法传递参数传值还是传址的问题

    这几天重构项目代码遇到一个疑问:可不可以在方法A中定义一个boolean变量b为false,然后A调用方法C把b传递到C方法中经过一些列业务判断后修改为true,C执行结束后A方法中b的值还是原来的f ...

  3. Java方法的参数是按值传递的.【转】

    在Java中,所有的方法参数,都是"按值传递". 有那么一种说法,Java中基本类型是按值传递,对象是按引用传递.这个说法其实是不确切的,确切的说法是 Java中基本类型将值作为参 ...

  4. 关于Java方法的参数

    刚好看到C++的函数这块,说C++中除了引用类型的形参,其他都是实参的副本(个人总结). 隐约记得Java中方法的参数也是这么回事,于是手动测试一番. 结果 Java中方法的参数都是值传递,哪怕是引用 ...

  5. JAVA方法中参数到底是值传递还是引用传递

    当一个对象被当作参数传递到一个方法后,在此方法内可以改变这个对象的属性,那么这里到底是值传递还是引用传递? 答:是值传递.Java 语言的参数传递只有值传递.当一个实例对象作为参数被传递到方法中时,参 ...

  6. java方法可变参数研究

    1 问题引出 (1)缘由 最近在研究如何在项目中引入Redis缓存,于是遇到可变参数这个疑惑点,之前没有好好研究过,为了避免项目后期出现问题. (2)项目相关技术 SpringBoot Redis K ...

  7. java方法---可变参数

    可变参数 在方法的声明中,在指定参数类型后面加一个...(省略号) 一个方法中只能指定一个可变参数,它必须是方法的最后一个参数,任何普通参数必须在它之前声明:

  8. java方法可变参数的写法

    jdk1.5之后出现的,该写法避免了当有多个不同个数的参数方法时,对方法的重载.其实就是数组. package com.shipin; /** * @author QiaoJiafei * @vers ...

  9. JAVA方法传递参数:传值?传引用?

    先来看下面这三段代码: //Example1: public class Example1 { static void check(int a) { a++; } public static void ...

随机推荐

  1. 使用ubuntu搭建时间机器备份服务

    如何在ubuntu下搭建时间备份服务 折腾了很久,终于可以了. 请严格按照下面的方式来操作. 真正明白问题的,可以按照自己的思路来. 我用的是ubnutu 16.04 安装配置netatalk sud ...

  2. 如何实现一个IOS网络监控组件

    此文由作者朱志强授权网易云社区发布. Mobile Application Monitor IOS组件设计技术分享 背景 应用程序性能管理Application Performance Managem ...

  3. Android优化之内存优化倒计时篇

    本文来自网易云社区 作者:聂雷震 本篇文章介绍的内容是如何在安卓手机上实现高效的倒计时效果,这个高效有两个标准:1.刷新频率足够高,让用户觉得这个倒计时的确是倒计时,而不是幻灯片:2.不能占用太多的内 ...

  4. iOS 设置 (plist)

    Architectures:

  5. Django的views视图系统

    老师的博客:http://www.cnblogs.com/liwenzhou/articles/8305104.html 以看老师的博客为主 一个视图函数(类),简称视图,是一个简单的Python 函 ...

  6. 466. Count The Repetitions

    Define S = [s,n] as the string S which consists of n connected strings s. For example, ["abc&qu ...

  7. BZOJ3729: Gty的游戏(伪ETT)

    题面 传送门 前置芝士 巴什博奕 \(Nim\)游戏的改版,我们现在每次最多只能取走\(k\)个石子,那么\(SG\)函数很容易写出来 \[SG(x)=mex_{i=1}^{\min(x,k)}SG( ...

  8. [JS] js 判断用户是否在浏览当前页面

    var hiddenProperty = 'hidden' in document ? 'hidden' : 'webkitHidden' in document ? 'webkitHidden' : ...

  9. [javascript]—jQuery解析本地 XML 文档

    Create a jQuery object using an XML string and obtain the value of the title node. <!doctype html ...

  10. String 源码浅析————终结篇

    写在前面 说说这几天看源码的感受吧,其实 jdk 中的源码设计是最值得进阶学习的地方.我们在对 api 较为熟悉之后,完全可以去尝试阅读一些 jdk 源码,打开 jdk 源码后,如果你英文能力稍微过得 ...