对于 Java 参数传入 null 判断思考

版权声明:本文章原创于 RamboPan ,未经允许,请勿转载。

对于 Java 参数传入 null 判断思考


最近重温了《Effective Java》之后,在日常工作中写代码时感觉也会慢一点,多一点思考这样是不是比较合理。

在一个常见的情况下迟疑了下:在定义方法时,经常需要传入非基本类型的参数。

	//定义一个简单的类
	class Hello{
		public void say(){
			System.out.print("Hello");
		}
	}

	//类似这种情况,需要传入一个对象进行它的操作
	public void test01(Hello hello) {
		hello.say();
	}

按理说我们会在这里进行一个判断,判断是否是空,以防我们在后续使用它的方法时出现空指针。就像这样进行一次判断。

	//类似这样的情况,传一个简单的参数
	//对字符进行判断一下,如果无效就不进行下一步操作
	public void test01(Hello hello) {
		if(hello == null){
			……
			return;
		}
		hello.say();
	}

一:按照一些编程书中的意见,我们最好不要做这些容错机制,如果是第一次调用该方法时,参数传入错误就直接报错,而不是后面发现没有输出 “Hello” 这个字符的时候再来寻找哪里的问题,这样就减少了调试的成本。

二:但是这样一想,要是这段代码自己测着没什么问题,其他懂代码的同事用了之后肯定也会懂。但如果这段代码在你自己的程序里,其他应用传入错误参数时,让你的程序崩溃了,虽然是别人的问题,但是你说是你慌一点还是别人慌一点 …… 🙃

所以感觉这还是要区分场景的,比如在前一种情况下,就是需要直接把问题暴露出来,而在后一种情况,类似于需要保证程序的健壮性,那么还是需要做容错的处理。


在写一个工具类的时候想到有些 Java 框架,在内部使用时,就进行了非空判断的操作。
比如 RxJava 中有个判断工具。

	//如果不为空则直接返回该对象
	public static <T> T checkNull(T t){
		if(t == null)
			throw new NullPointerException("空指针啦,亲");
			
		return t;	
	}
	
	//这样写也是可以的,只是在没法嵌套在其他函数参数中。
	public static <T> void checkNull(T t){
		if(t == null)
			throw new NullPointerException("空指针啦,亲");
	}

	//======================= 示范 ===================

	//定义一个简单的类
	class Hello{
		public void say(){
			System.out.print("Hello");
		}
	}

	//类似这种情况,需要传入一个对象进行它的操作
	public void test01(Hello hello) {
		hello.say();
	}

	//这种是使用带返回的 checkNull ,可以进行嵌套使用
	public void checkNull01(){
		Hello hello = new Hello();
		test01(checkNull(hello));
	}

	//这种是不带返回的 checkNull ,不能进行嵌套使用,就是多了一行 …… 
	public void checkNull02(){
		Hello hello = new Hello();
		checkNull(hello);
		test01(hello);
	}

其实这种的好处就是在调试期间,如果出错的话,肯定会第一时间找到出错位置,就是类似上面说的第一种情况。

既然要做这种判断非空的工具,那就做一个通用的方法,再做几个简单封装的方法,是常规思路嘛。比如这里我们可以加入需要的 Exception 类型,出错的 String 提示,大概就要这样调用效果。

	//定义一个简单的类
	class Hello{
		public void say(){
			System.out.print("Hello");
		}
	}

	//测试
	public void test01() {
		Hello hello = new Hello();
		hello = checkNull(hello,NullPointerException.class,"空指针啦,亲");
	}

第一个是需要检测的对象,第二个是异常的类型,第三个就是提示语。
虽然我们这里是只检测是否为空,按理说就应该抛空指针,你还要抛什么其他的花样 ?好奇的你肯定会问。

虽然直观上是空指针,但是如果你在一个方法中对成员变量进行判断,可能这个对象已经完成使命,执行了一些释放的操作,成员变量已经被置空,那么此时如果抛出 IllegalStateException 加上说明,比如 “ xx 已经被释放了,请检查状态 ” 是不是更合适一点 ?所以还是灵活一点好。(毕竟泛型帅嘛,啊哈哈哈)

	//通用的接口
   public static <T> T checkNull(T t,Class<? extends Exception> eClazz,String msg){
        if(t == null){
            try {
                Constructor<? extends Exception> eCons = eClazz.getDeclaredConstructor(String.class);
                eCons.setAccessible(true);
                throw eCons.newInstance(msg);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
        return t;
    }

第一个参数是对于对象的判断,第二个参数是采用泛型限定了 Exception 类及其子类,第三个参数针对错误的提示。

因为前面的一些限定,所以后续的四个 Exception 中没有再次抛出(比如大部分 Exception 子类,都有单个 String 的构造函数;虽然都是 public,此处也加上了访问权限设置 ,所以忽略了这些情况)

编译器没有提示错误,仿佛没有什么不对,于是编译跑起来试试,毕竟看着对和跑得起来是两回事 …… 果然,编译的途中出错了(男人的直觉 😂)。

错误: 未报告的异常错误CAP#1; 必须对其进行捕获或声明以便抛出
其中, CAP#1是新类型变量:
CAP#1从? extends Exception的捕获扩展Exception

说实话这个真的有点摸不着的头脑,感觉逻辑上也没错啊,这究竟是怎么肥事 …… 🤔

直接搜这个错误,还没什么参考,于是把几个关键字都试了一下,看了别人的意见有点启发。

Exception 分为 编译期的错误 与 运行时的错误 ,像 NullPointerException 这种就属于运行时的错误,如果是 Exception 、 IOException 这种一定是需要自己处理了之后才能通过编译的。

处理的方式就是两种,正如报错的提示:必须对其进行捕捉或者声明以便抛出。

	//声明:当其他方法调用此方法时需要它处理异常
	public void say() throws Exception {
		……
	}

	//捕捉:这个方法内部已经处理了,其他方法调用时不需要处理
	public void say(){
		try{
			……
		}catch(Exception e){
			……
		}
	}

现在我们来做个试验,测试下是不是正如我们猜想的那种:这里需要抛出一个 RuntimeException ,而不是 Exception。

	public void test01(){
		throw new Expcetion("试一试,好像不行");
	}

提示: Unhandled Exception: java.lang.Exception

如果我们按照两种处理异常的方式处理一下,那么就没有问题了,也能顺利通过编译。

当然,这不是我们要的结果,此处我们想要的就是在运行时,遇到问题直接抛出(就是这么刚)。

那我们把泛型限定条件改一下: Class<? extends Exception> 改为 Class<? extends RuntimeException>

然后再进行编译就没问题了,当然 Constructor 对应的泛型限定符也需要对应的修改。


分享下平时思考的笔记,如有不对之处,欢迎指出讨论。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章