约束类型

类型形参可以由任何类类型取代.这样做对于大多数情况都适合,但是,有时需要限制传递给类型形参的类型.例如,假定要创建一个能够保存数值并且执行各种数学函数(如计算倒数或获得小数部分)的泛型类.而且,你希望对任何数值,包括整数,浮点数和双精度数执行计算.因此,需要使用类型形参指定通用类型的数值.要创建这样的类,可以尝试这样做:
class NumericFns<T>
{
    T num;
    
    NumericFns(T n)
    {
        num = n;
    }
    
    double reciprocal()
    {
        return 1 / num.doubleValue();
    }
    
    double fraction()
    {
        return num.doubleValue() -  num.intValue();
    }
}
但是.NumericFns不能通过编译,因为两个方法都产生编译时错误.先看看reciprocal()方法,他获取num的倒数.为此,必须用1除以num的值.num值通过调用doubleValue()方法获得,doubleValue()获得保存在num中的数值对象的double版本.由于所有的数值类,如Integer和Double都是Number的子类,而Number定义了doubleValue()方法,所以该方法对所有的数值封装器类有效.问题是编译器无法知道你要创建只使用数值类型的NumericFns对象,因此,当试图编译NumericFns时,会报告错误,指示doubleValue()方法未知.对于fraction()也会发生同样的错误,他需要调用doubleValue()和intValue().两个调用都报告方法未知.为了解决该问题,需要有某种办法来确保只实际传递数值类型给T.而且,需要有某种方法来确保只有数值类型被实际传递.
为此,Java提供了约束类型(bounded types).在指定类型形参时,可以创建一个上层约束(upper bound)来声明一个超类,所有的类型实参都必须继承该超类.这是通过在指定类型形参时使用extends子句完成的,如下所示:
<T extends superclass>
该语句指定T只能由superclass或superclass的子类替换.因此,superclass定义了包含superclass在内的上层约束.
可以使用上层约束来修正前面的NumericFns类,方法是指定Number作为上层约束,.如下所示:
public class test2
{
    // @param args
    public static void main(String args[])
    {
     NumericFns<Integer> iOb = new  NumericFns<Integer>(5);
     System.out.println("reciprocal of iob is  "+iOb.reciprocal());
     System.out.println("Fractiona component of  iob is "+iOb.fraction());
     
     NumericFns<Double> dOb = new  NumericFns<Double>(6.66);
     System.out.println("reciprocal of dob is  "+dOb.reciprocal());
     System.out.println("Fractiona component of  dob is "+dOb.fraction());
    }
}
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();
    }
}
程序的输出如下所示:
reciprocal of iob is 0.2
Fractiona component of iob is 0.0
reciprocal of dob is 0.15015015015015015
Fractiona component of dob is 0.6600000000000001
注意,NumericFns现在使用下面的语句声明:
class NumericFns<T extends Number>
由于类型T现在由Number约束,因此Java编译器就会知道所有类型T的对象都能够调用doubleValue(),因为他是Number声明的一个方法.这样做很方便,而且还有额外的好处:约束T还可以防止创建非数值的NumericFns对象,例如,如果删除程序末尾几行语句的注释,然后重新编译程序,将会产生编译时的错误,因为String不是Number的子类.
约束类型在需要确保一种类型形参与另一种兼容时特别好用.例如,下面的类Pair保存了两个必须彼此兼容的对象:
class Pair<T,V extends T>
{
    T frist;
    V second;

    Pair(T a,V b)
    {
        first = a;
        second = b;
    }
}
Pair有两个类型形参:T和V,且V扩展了T.因此,V必须与T相同或是其子类.这样做可以确保Pair构造函数的两个实参要么相同,要么相关.下面的构造函数是有效的:
     Pair<Integer,Integer> x = new  Pair<Integer,Integer>(1,2);
     Pair<Number,Integer> y = new  Pair<Number,Integer>(10.4,2);
下面的构造函数是无效的:
     Pair<String,Integer> y = new  Pair<String,Integer>("aaa",2);
这里,String不是Number的子类,他违反了Pair指定的约束.