<? extends SomeClass>与<T extends SomeClass>的区别

看apache parquet源码时,发现代码各种泛型嵌套,有必要系统整理一下关于泛型的各种知识,在此做一总结。

首先是名词对应表,不需要记住右边的名字,但需要知道左边的各种用法

List<String> —- 参数化的类型
List<E> —- 泛型
List<?> —- 无限制通配符类型
<E extends SomeClass> —- 有限制类型参数
List <? extends SomeClass>—- 有限制通配符类型
<T extends Comparable<T>> —– 递归类型限制
static <E> List<E> asList(E[] a) —- 泛型方法

下面自己的实验包括代码,标号1是解决题目里描述的问题,其余的标号也是自己遇到的一些关键的问题。

疑问&要解释的东西


1:<E extends ClassA><? extends ClassA>有什么区别?
  • 答:当我第一次接触这两名词时,感觉他们的功能是一样的,T可以代表任意的子类,?也可以代表任意的子类。
    首先我们明确一下两边的名字,限制类型 & 通配符类型,<E extends ClassA>表示后续都只能使用E进行某些判断或操作,而<? extends ClassA>?表示后续使用时可以是任意的。
    举个<E extends ClassA>最常见的例子,用于比较操作,比如返回“最大值”,“最大值”的定义为:整型、浮点型返回最大值,字符串返回字典序最大者,由于想调用compareTo函数,我们让所有参数都继承Compareble,即T extends Comparable<T>,整个测试代码如下
package test;

/**
 *  定义了 <T extends someClass>, 
 *  里面的代码便只能用somClass的子类T进行比较或其他操作。
 */
public class MaximumTest {

   // determines the largest of three Comparable objects
   public static <T extends Comparable<T>> T maximum(T x, T y, T z) {                      
      T max = x; // assume x is initially the largest       
      if ( y.compareTo( max ) > 0 ) {
         max = y; // y is the largest so far
      }
      if ( z.compareTo( max ) > 0 ) {
         max = z; // z is the largest now                 
      }
      return max; // returns the largest object   
   }

   public static void main(String args[])  {
      System.out.printf( "Max of %d, %d and %d is %d\n\n", 
                   3, 4, 5, maximum( 3, 4, 5 ) );

       System.out.printf( "Maxm of %.1f,%.1f and %.1f is %.1f\n\n",
                   6.6, 8.8, 7.7, maximum( 6.6, 8.8, 7.7 ) );

       System.out.printf( "Max of %s, %s and %s is %s\n","pear",
         "apple", "orange", maximum( "pear", "apple", "orange" ) );
   }
}

注释里已经写清楚了,我们只能用T类型来进行一些操作,我们不能把T替换成?,因为?并不是一个类名,它只是一个通配符,然后举个<? extends ClassA>的例子。

比如我们有一个Stack类,类里提供一个pullAll方法,我们想把一系列元素全部放到堆栈中,如下方法

   // Stack定义
   public class Stack<E> {
       public Stack();
       public E pop();
       public boolean isEmpty();
   }

   // ...
   public <E> void pushAll(Iterable<E> src) {
       for(E e : src) {
           push(e);
       }
   }

这个方法编译时没问题,Iterable src的元素类型与堆栈的类型完全匹配就没有问题。但是假如有一个Stack<Number>调用了push(intVal),这里的intVal是Integer类型,这是可以的,因为Integer是Number的一个子类型,但下面的代码会报编译错误,

Stack<Number> numberStack = new Stack<Number>();
Iterable<Integer> integers = "...";
numberStack.pushAll(integers);

因为在Java中,参数化类型是不可变的。所以现在我们的通配符类型就派上用场了,代码如下

public void pushAll(Iterable<? extends E> scr) {
    for( E e : src) {
        push(e);
    }
}   

此处就必须用通配符?,代表泛型的泛指“E的某个子类型的Iterator接口”。
扩张阅读:Stackoverflow : 区别


2、List<Object> o = new ArrayList<Long>(); 报错
  • 不同于数组Object[] o,Long[] o,因为List<Type1>List<Type2>不互为子类型or超类型

3、无法创建泛型数组。

4、泛型方法的使用
  • static <E> List<E> asList(E[] a) —- 泛型方法
    参考这篇文章Java中的泛型方法的解释,注意区分Class与实例。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值