Java 返回数组或集合的方法设计

####1. 前言####

面向对象的其中一个很重要特征是封装,最简单的封装可能就是对一组属性的封装,然后只提供了setter和getter方法,比如下面这个对象:

public class Dog {
	
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
}

而熟悉Eclipse的人肯定知道,Eclipse有一种快捷方式可以快速的自动生成对应的Getter和Setter方法,右键 -> Source -> Generate Getters and Setters -> 选中要生成的属性 -> OK 即可,这着实能够提高工作中的效率,我相信很多人在工作中对于一些简单的值对象都是采用着这种方式,然后就不再修改,这样固然没错,但是对于一些包含List或Map等对象的时候,我相信Getter应该有更好的写法。

####2. 实例####
比如一个包含List列表的对象,如果使用最简单的封装可能就是下面这个样子:

public class Country {
	
	private List<String> citys;

	public List<String> getCitys() {
		return citys;
	}

	public void setCitys(List<String> citys) {
		this.citys = citys;
	}

}

那么如果我使用它的属性citys的时候就可能会是下面这个样子:

public static void main(String[] args) {
	Country country = new Country();
	
	// ...
	
	List<String> citys = country.getCitys();
	// 先判断是否为空
	if (citys != null) {
		// for (String city : citys) ...
	}
}

注意到,在使用citys前,必须先判断它是否为空,否则不管是调用它的方法或者是遍历,都可能会引起java.lang.NullPointerException空指针异常,除非你在使用前能够确保它一定不会为null。

要消除这种使代码产生冗余或者说麻烦的方法,可以在定义属性的时候便初始化,比如这样:

public class Country {
	
	private List<String> citys = new ArrayList<String>();

	// Getter and settter
}

但是这样也许会显得有点鸡肋,因为实例化一个对象并不一定要使用到这个属性,如果在定义属性的时候便初始化值,在很少使用到这个属性的时候势必不是一个极好的选择,当然这样也还好,不过还有另外一种方法,就是在Getter方法中做手脚,就像这样:

public class Country {
	
	private List<String> citys;

	public List<String> getCitys() {
		return citys == null ? Collections.emptyList() : null;
	}

	public List<String> getCitysDirect() {
		return citys;
	}

	// Setter
}

这里在getCitys()方法中,如果citys为空,就返回一个空的ArrayList列表,而不是null,与此同时,提供
getCitysDirect()方法以满足需要直接获取属性值的时候,那么在使用时就不需要在判断是否为null了,也不必初始化了却不使用的问题,使用必然更放心。

public static void main(String[] args) {
	Country country = new Country();
	// Do something
	
	List<String> citys = country.getCitys();
	for (String ci : citys) {
			
	}
}

同样的,对于Map这样的基础容器,也可以类似这样去做。而针对Setter方法,可以为List或Map额外提供add(e),put(key, value)等方法,这里便不再赘述了。

参考资料:阿里巴巴开源软件 Druid https://github.com/alibaba/druid

####3. Effective JAVA####
在《Effective JAVA》中的第43条:返回零长度的数组或者集合,而不是null,正好也印证这一点,摘抄部分如下:

像下面这样的方法并不少见:

private final List&lt;Cheese&gt;  chessesInStock = ...;
 
public Chess[] getCheese() {
     if (cheeseInStock.size() == 0) {
         return null;
     }
}

把没有奶酪(cheese)可买的情况当作是一种特例,这是不合常理的。这样做会要求客户端中必须有额外的代码来处理null返回值,例如:

Cheese[] cheeses = shop.getCheese();
if (cheeses != null && 					
		Arrays.asList(cheeses).contains(Cheese.STILTON)) {
	// ...
}

而不是下面这段代码:

if (Arrays.asList(cheeses).contains(Cheese.STILTON)) {
	// ...
}

对于一个返回null而不是零长度数组或者集合的方法,几乎每次用到该方法时都需要这种曲则的处理方式。这样做很容易出错,因为编写客户端程序的程序员可能会忘记写这种专门的代码来处理null返回值。这样的错误也许几年都不会被注意到,因为这样的方法通常返回一个或多个对象。返回null而不是零长度的数组也会使返回数组或者集合的方法本身变得更加复杂,这一点虽然不是特别重要,但是也值得注意。

集合值的方法也可以做成在每当需要返回空集合时都返回同一个不可变的空集合。如下所示:

public List<Cheese> getCheeseList() {
	if (cheesesInStock.isEmpty())
		return Collections.emptyList(); // Always returns same list
	else 
		return new ArrayList<Cheese>(cheesesInStock);
}

简而言之,返回类型为数组或集合的方法没有理由返回null,而不是返回一个零长度的数组或者集合。(PS: 这里对这句话的翻译表示怀疑)

最后,针对上面所说的“每当需要返回空集合时都返回同一个不可变的空集合”做下解释,之所以要返回一个同一个不可变的集合,其主要目的在于:**在把一个指向内部可变组件的引用返回给客户端之前,做保护性拷贝,以防止内部组件被外部类修改,**具体可参见《Effective JAVA》第39条:必要时进行保护性拷贝。

以上。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值