使用通配符实参

尽管类型安全性很有用,但是有时却会妨碍完全可以接受的构造.例如,对于上面的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对象.