最新消息:热烈庆祝IT小记上线!

Java泛型类型参数的界限

作用

1. 限制类型参数的类型

如要定义一个类IntegerPair来存取元素类型为Integer的对偶,此类可写成如下的形式:

public class IntegerPair<T extends Integer>
{
   public IntegerPair() { first = null; second = null; }
   public IntegerPair(T first, T second) { this.first = first;  this.second = second; }

   public T getFirst() { return first; }
   public T getSecond() { return second; }

   public void setFirst(T newValue) { first = newValue; }
   public void setSecond(T newValue) { second = newValue; }

   private T first;
   private T second;
}

类型参数后的extends关键字表示T 的上界为Integer类型,编译器在类型擦除阶段会将T替换成Integer,生成的字节码如下所示:

Compiled from "IntegerPair.java"
public class generic.bound.IntegerPair<T extends java.lang.Integer> {
  public generic.bound.IntegerPair();
    descriptor: ()V
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: aload_0
       5: aconst_null
       6: putfield      #2                  // Field first:Ljava/lang/Integer;
       9: aload_0
      10: aconst_null
      11: putfield      #3                  // Field second:Ljava/lang/Integer;
      14: return

  public generic.bound.IntegerPair(T, T);
    descriptor: (Ljava/lang/Integer;Ljava/lang/Integer;)V
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: aload_0
       5: aload_1
       6: putfield      #2                  // Field first:Ljava/lang/Integer;
       9: aload_0
      10: aload_2
      11: putfield      #3                  // Field second:Ljava/lang/Integer;
      14: return

  public T getFirst();
    descriptor: ()Ljava/lang/Integer;
    Code:
       0: aload_0
       1: getfield      #2                  // Field first:Ljava/lang/Integer;
       4: areturn

  public T getSecond();
    descriptor: ()Ljava/lang/Integer;
    Code:
       0: aload_0
       1: getfield      #3                  // Field second:Ljava/lang/Integer;
       4: areturn

  public void setFirst(T);
    descriptor: (Ljava/lang/Integer;)V
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #2                  // Field first:Ljava/lang/Integer;
       5: return

  public void setSecond(T);
    descriptor: (Ljava/lang/Integer;)V
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #3                  // Field second:Ljava/lang/Integer;
       5: return
}

可见,IntegerPair支持类型为Integer及其子类型的元素,故BoundTest不会通过编译。

public class BoundTest {
    public static void main(String[] args) {
        IntegerPair integerPair = new IntegerPair();
        integerPair.setFirst("text");
    }
}

由于IntegerPar的类型参数T的界限为Integer,IntegerPair setFirst的签名为(Ljava/lang/Integer;)V,故编译BoundTest时会报错,因为integerPar setFisrt传入的是String类型:

BoundTest.java:12: error: incompatible types: String cannot be converted to Integer
        integerPair.setFirst("text");
                             ^
Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output
1 error

2.实现通用算法

可以通过类型参数调用界限的方法,如NaturalNumber所示:

public class NaturalNumber<T extends Integer> {

    private T n;

    public NaturalNumber(T n)  { this.n = n; }

    public boolean isEven() {
        return n.intValue() % 2 == 0;
    }

    // ...
}

由于类型参数T的界限为Integer,故可在T上调用Integer的方法,如initValue. 能调用界限的方法是实现通用算法的关键。考虑求某个列表中大于某个元素的算法:

public static <T> int countGreaterThan(T[] anArray, T elem) {
    int count = 0;
    for (T e : anArray)
        if (e > elem)  // compiler error
            ++count;
    return count;
}

由于运算符>只能在基础数据类型如short, int, long, double, float, byte和char上进行操作,无法对Object进行>操作,故编译器会报错:

GenericAlgorithm.java:19: error: bad operand types for binary operator '>'
            if (e > elem)  // compiler error
                  ^
  first type:  T
  second type: T
  where T is a type-variable:
    T extends Object declared in method <T>countGreaterThan(T[],T)
1 error

这个问题会导致countGreaterThan方法不通用。给类型参数加上一个界限,可以很好的解决这个问题:

public interface Comparable<T> {
    public int compareTo(T o);
}

新的通用算法如下:

public static <T extends Comparable<T>> int countGreaterThan(T[] anArray, T elem) {
    int count = 0;
    for (T e : anArray)
        if (e.compareTo(elem) > 0)
            ++count;
    return count;
}

多界限

一个类型参数可有多个界限,表达方式如下:

<T extends B1 & B2 & B3>

类型变量T的类型为界限列表中任一类型的子类.zhu’ru注意:如类型参数中的某个界限为类,必须放在界限列表中的第一个位置,如MultiBounds.java所示:

public class MultiBounds {
    static class A {
        public void a() {

        }
    }

    interface B {
        void b();
    }

    interface C {
        void c();
    }


    static class D <T extends A & B & C> {
        public void d(T t) {
            t.a();
            t.b();
            t.c();
        }
    }

    public static void main(String[] args) {
        D d = new D();
        d.d(new A());
    }
}

D中类型参数的界限类表声明成如下形式,B为接口,类A放在界限列表的第二个位置,故编译器将会报错:

D <T extends B & A & C> 

小结

类型参数的界限可以用来限制类型变量的类型,是实现通用算法的关键;类型变量可以有多个界限。

猜您喜欢

备案号:苏ICP备12016861号-4