尽管类型安全性很有用,但是有时却会妨碍完全可以接受的构造.例如,对于上面的NumericFns类,假定需要添加方法absEqual(),如果两个NumericFns对象包含的数值的绝对值相等,则返回true.而且,你希望该方法能够正常工作,而不管每一个对象保存的数值的类型是什么.例如,如果一个对象包含Double值1.25,另一个对象包含Float值-1.25,那么absEqual()方法仍旧能够返回true.实现absEqual()的途径之一是为其传递NumericFns实参,然后把该实参的绝对值和调用对象的绝对值相比较,如果值相等,则返回true.例如,可以按如下方式来调用absEqual():
NumericFns<Double> dob = new NumericFns<Double>(1.25); NumericFns<Float> fob = new NumericFns<Float>(-1.25); if(dob.absEqual(fob)) { System.out.println("Absolute values are the same"); } else { System.out.println("Absolute values differ"); }
初看起来,创建absEqual()似乎很容易.遗憾的是,当声明NumericFns类型的形参时就会出现问题.应该NumericFns的类型形参指定什么类型?你可能会首先想到下面的解决方案,把T用作类型形参.
boolean absEqual(NumbericFns<T> ob) { if(Math.abs(num.doubleValue()) == Math.abs(ob.num.doubleValue())) return true; return false; }
其中,标准方法Math.abs()用来获得每一个数的绝对值,然后对值做比较.问题是他只对其类型与调用对象相同的其他NumericFns对象起作用.例如,如果调用对象的类型是NumericFns<Integer>,则形参ob必须也是NumericFns<Integer>类型.他不能用来比较其他类型,如NumericFns<Double>类型的对象.因此,该方法无法产生通用的(也就是泛型的)解决方案.
要创建泛型的absEqual()方法,必须使用Java的另一个泛型特征:通配符实参.通配符实参由”?”指定,代表未知的类型.下面是使用通配符编写的absEqual()方法的方式之一:
boolean absEqual(NumericFns<?> ob) { if(Math.abs(num.doubleValue()) == Math.abs(ob.num.doubleValue())) return true; return false; }
其中,NumberFns<?>表示匹配任何类型的NumericFns对象,从而允许任意两个NumericFns对象比较其绝对值,下面的程序对此做了演示:
public class test2 { // @param args public static void main(String args[]) { NumericFns<Double> dob = new NumericFns<Double>(-6.0); NumericFns<Integer> iob = new NumericFns<Integer>(6); NumericFns<Long> lob = new NumericFns<Long>(5L); if(dob.absEqual(iob)) { System.out.println("Absolute values are the same"); } else { System.out.println("Absolute values differ"); } if(dob.absEqual(lob)) { System.out.println("Absolute values are the same"); } else { System.out.println("Absolute values differ"); } } } class NumericFns<T extends Number> { T num; NumericFns(T n) { num = n; } double reciprocal() { return 1 / num.doubleValue(); } double fraction() { return num.doubleValue() - num.intValue(); } boolean absEqual(NumericFns<?> ob) { if(Math.abs(num.doubleValue()) == Math.abs(ob.num.doubleValue())) return true; return false; }
程序的输出如下所示:
Absolute values are the same Absolute values differ
在该程序中,注意下面两行对absEqual()的调用:
if(dob.absEqual(iob)) if(dob.absEqual(lob))
在第一个调用中,iOb是一个NumericFns<Integer>类型的对象,dOb是一个NumericFns<Double>类型的对象.但是,通过使用通配符,就可以让dOb在调用absEqual()时传递iOb.相同情况也可以应用于第二个调用,该调用中传递NumeriFns<long>类型的对象.
最后要提示的一点是:通配符并不会影响能够创建的NumeriFns对象的类型,理解这一点很重要,这是由NumericFns声明中的extends子句控制的.通配符只是匹配任何有效的NumericFns对象.