您的位置:首页 > 其它

重写方法时需要注意的问题

2015-05-13 21:53 239 查看
在继承体系中,重写(override)方法并添加自己的功能是很常见的方式。但是如果对所要重写的方法理解不当时可能会出现一些意想不到的问题。

例如,在android中继承Activity后,希望添加一个这样的功能:记录页面跳转的路径,用作统计数据或其他功能使用。也就是说,当从Activity a1跳转到Activity a2,再到Activity a3,这个过程之后得到的路径应该是a1--->a2--->a3。对于这个功能的实现可考虑使用一下方式:

假设客户端的所有Activity继承自BaseActivity,重写BaseActivity的startActivity()方法。在这个方法中调用自定义方法——prepareNextIntent(),这个方法中将会取得由上一级Activity传来的路径链,并加入当前Activity的pageRef,再准备传给下一个即将启动的Activity。

这样,我们就可以获得页面跳转的路径。

此时,我们可能发现只重写startActivity()方法并不够。因为启动Activity的时候,还可能会调用startActivityForResult()方法。因此,我们在BaseActivity中再重写startActivityForResult()方法,并在其中调用我们的prepareNextIntent()。这样之后,我们就既可以使用startActivity()方法,又可以使用startActivityForResult()方法来启动Activity并记录相应的页面跳转路径。

接下来,当我们使用startActivity()来启动Activity几次之后,发现页面跳转路径并不是我们期望的a1--->a2--->a3,而变成了a1--->a1--->a2--->a2--->a3--->a3。对比即可发现,问题在于每跳转一次页面,当前页面的pageRef被记录了两次。这个问题的原因就在于:

我们重写的startActivity()方法内部本身会调用startActivityForResult()。

当我们重写了startActivityForResult()之后,每次调用startActivity()方法时,相当于prepareNextIntent()被调用两次——第一次是在重写之后的startActivity()中调用,第二次是startActivity()内部调用的startActivityForResult()方法(这个方法被我们重写了)中。

因此,了解即将要重写的方法的内部逻辑,才能避免出现此类问题。

类似的问题常出现在继承集合类时,如HashSet。

例如,我们需要对加入的元素计数,此时考虑继承HashSet得到InstrumentedHashSet类,在这个类声明一个成员变量count用来计数。然后在add()和addAll()等添加元素的方法中对这个计数变量count进行累加处理:

public class InstrumentedHashSet<E> extends HashSet<E>{
private int count=0;
……
@override
public boolean add(E e){
count++;
return super.add(e);
}
@override
public boolean addAll(Collection<? extends E> c){
count += c.size();
return super.addAll(c);
}
……
}


之后我们使用这个新定义的带有计数功能的InstrumentedHashSet。当我们创建这个类的实例并调用addAll()方法来添加一个String类型的数组之后,奇怪的事情发生了……

InstrumentedHashSet<String> s = new InstrumentedHashSet<String>();

s.addAll(Arrays.asList("aa", "bb", "cc"));

此时我们期望的count应该是3,但实际上返回的是6。

问题原因如上述的startActivity()和startActivityForResult()方法重写问题,即这里的addAll()方法内部实现是基于add()方法的。我们调用addAll()时先对count加了数组的长度,此时count为3。之后又调用到被重写的add()方法,每个元素调用一次。这样count就被累加到了6。

对于上述这个HashSet的问题,《Effective Java》一书中给出的解决方案时,使用复合(composition)。即把现有类(HashSet)作为新类(InstrumentedHashSet)的一个组件,而不是直接扩展现有类。但对于Activity的那个例子来说,个人认为使用复合并不见得合适,因为复合之后需要做的就是转发(forwarding),而这个转发动作如果涉及到现有类的很多方法的话,并不是件容易的事情。因此,还是应该从新功能的其他实现方式考虑。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: