Java 泛型 泛型数组

@author ixenos

  • 先给结论
    • 不能(直接)创建泛型数组
    • 泛型数组实际的运行时对象数组只能是原始类型( T[]为Object[],Pair<T>[]为Pair[] ),而实际的运行时数组对象可能是T类型( 虽然运行时会擦除成原始类型 )
    • 一般解决方案:(泛型数组包装器):使用ArrayList收集泛型数组对象的对象元素,如ArrayList<T>、ArrayList<Pair<String>>
      • 将获得数组的行为,以及由泛型提供的编译期的类型安全
  • 直接创建泛型数组不能通过编译,而转型对象数组通过编译但是不能在JVM运行
      • public class ArrayOfGeneric{
        static Generic<Integer>[] gia;
        @SupperssWarnings("unchecked")
        public static void main(String[] args){
        gia = (Generic<Integer>[])new Generic[100]; // 通过类型转换匿名对象
        //! gia[0] = new Object(); //编译不通过,不能(直接)创建泛型数组实例
        }
        }
    • 问题在于数组将跟踪他们的实际类型,而这个类型是在数组被创建时确定的,因此,即使gia已经被转型为Generic<Integer>[],但这个信息只存在于编译期(并且如果没有@SuppressWarning("unchecked")注解,将得到这个转型的警告)。在运行时,它仍旧是Object数组
    • 因此,成功创建泛型数组的唯一方式就是创建一个被擦出类型的新数组,然后对其转型(而且是在运行时转型)
      •  直接对整个数组强制转型,在编译时依旧会被擦除掉类型!所以应该在运行时转型,而这时最好的办法就是使用一个泛型数组包装器,维护一个原始类型的数组,通过数组入口方法进行元素编译期的类型安全检测(对应返回值)和强制类型转换(对于运行时不重要),从而保证类型安全。
    • 对整个数组强制转型的例子(错误方法)
    • public class GenericArray<T> {
      private T[] array;
      @SupperessWarning("unchecked")
      public GenericArray(int sz) {
      array = (T[]) new Object[sz];
      }
      public void put(int index, T item) {
      array[index] = item;
      }
      public T get(int index) { return array[index]; }
      public T[] rep() { return array; } //应该在运行时出口做文章
      public static void main (String[] args){
      GenericArray<Integer> gai = new GenericArray<Integer>(10);
      // Integer[] ia = gai.rep(); //ClassCastException
      Object[] oa = gai.rep(); //只能返回对象数组类型为Object[]
      • 实际的运行时对象数组是Object[],而实际的运行时数组对象可能是T类型。
    • 因此,应该在运行时,数组对象的出口做转型输出,入口方法在编译期已实现类型安全,所以出口方法可以放心强制类型转换,保证成功。如下

      • public class GenericArray2<T> {
        private Object[] array; //维护Object[]类型数组
        @SupperessWarning("unchecked")
        public GenericArray2(int sz) {
        array = new Object[sz];
        }
        public void put(int index, T item) {
        array[index] = item;
        }
        public T get(int index) { return (T)array[index]; }//数组对象出口强转
        public T[] rep() { return (T[])array; } //运行时无论怎样都是Object[]类型
        public static void main (String[] args){
        GenericArray<Integer> gai = new GenericArray<Integer>(10);
        // Integer[] ia = gai.rep(); //依旧ClassCastException
        Object[] oa = gai.rep(); //只能返回对象数组类型为Object[]
        gai.put(0,11);
        System.out.println(gai.get(0)); // 11 ,出口成功转型
        }
        }
  •  通过反射在运行时构出实际类型为type[]的对象数组,避免了类型擦除,从而转换成功,无ClassCastException
import java.lang.reflect.*; 

public class GenericArrayWithTypeToken<T> {
private T[] array;
@SuppressWarning("unchecked")
public GenericArrayWithTypeToken(Class<T> type, int sz) {
array = (T[]) Array.newInstance(type, sz);//通过反射在运行时构出实际类型为type[]的对象数组,避免了类型擦除,从而转换成功,无ClassCastException
}
public void put(int index, T item){
array[index] = item;
}
public T get(int index) { return array[index]; }
public T[] rep() { return array; } //能成功返回了~
public static void main(String[] args) {
GenericArrayWithTypeToken<Integer> gawtt = new GenericArrayWithTypeToken<>(Integer.class, 10);
Integer[] ia = gawtt.rep(); //能成功返回了!
}
}

Java 泛型 泛型数组的更多相关文章

  1. 《徐徐道来话Java》(2):泛型和数组,以及Java是如何实现泛型的

    数组和泛型容器有什么区别 要区分数组和泛型容器的功能,这里先要理解三个概念:协变性(covariance).逆变性(contravariance)和无关性(invariant). 若类A是类B的子类, ...

  2. Java“禁止”泛型数组

    Java“禁止”泛型数组 原文:https://blog.csdn.net/yi_Afly/article/details/52058708 1. 泛型定义泛型编程是一种通过参数化的方式将数据处理与数 ...

  3. Java 泛型 五:泛型与数组

    简介 上一篇文章介绍了泛型的基本用法以及类型擦除的问题,现在来看看泛型和数组的关系.数组相比于Java 类库中的容器类是比较特殊的,主要体现在三个方面: 数组创建后大小便固定,但效率更高 数组能追踪它 ...

  4. Java泛型与数组深入研究

    Java中的泛型与数组平时开发用的很多,除了偶尔遇到"NullPointerException"和"IndexOutOfBoundsException"一般也不 ...

  5. (转载)Java里新建数组及ArrayList java不允许泛型数组

    java中新建数组: String[] s;//定义的时候不需要设置大小 s = new String[5];//为数组分配空间时就要设置大小   对于ArrayList, ArrayList< ...

  6. Java笔记--泛型总结与详解

    泛型简介: 在泛型没有出来之前,编写存储对象的数据结构是很不方便的.如果要针对每类型的对象写一个数据结构,     则当需要将其应用到其他对象上时,还需要重写这个数据结构.如果使用了Object类型, ...

  7. [改善Java代码]Java的泛型是类型擦除的

    泛型可以减少强制类型的转换,可规范集合的元素类型,还可以提高代码的安全性和可读性,正是因为有了这些优点,自从Java引入泛型之后,项目的编码规则上便多了一条,优先使用泛型. Java泛型(Generi ...

  8. Java 中泛型的全面解析(转)

    Java泛型(generics) 是JDK 5中引入的一个新特性,允许在定义类和接口的时候使用类型参数(type parameter).声明的类型参数在使用时用具体的类型来替换.泛型最主要的应用是在J ...

  9. Java中泛型 类型擦除

    转自:Java中泛型是类型擦除的 Java 泛型(Generic)的引入加强了参数类型的安全性,减少了类型的转换,但有一点需要注意:Java 的泛型在编译器有效,在运行期被删除,也就是说所有泛型参数类 ...

随机推荐

  1. RelativeLayout各个属性

    android:layout_above="@id/xxx"  --将控件置于给定ID控件之上 android:layout_below="@id/xxx"  ...

  2. Xcode插件推荐

    deriveddata-exterminator: A magic button in Xcode to exterminate the current project's DerivedData d ...

  3. C#调用winhttp组件 POST登录迅雷

    下面是封装好的winhttp类 using System; using System.Collections.Generic; using System.Linq; using System.Text ...

  4. break位于内循环。作用于外循环

    outer:for(int i = 0; i < 4; ++i)  {       inner:for(int j = 0; j < 4; ++j)       {           S ...

  5. CODE[VS]-求和-整数处理-天梯青铜

    题目描述 Description 求n个数的和 输入描述 Input Description 第一行一个整数n 接下来一行n个整数 输出描述 Output Description 所有数的和 样例输入 ...

  6. C# 图形普通处理,resize ,水印..

    网站中,对用户图片上传处理是很有必要的.对于一些常用的处理,对图片各种形式的压缩,各种形式的水印. 1.裁剪正方形头像方法 /// <summary> /// 正方型裁剪 /// 以图片中 ...

  7. HTTP Response Splitting攻击探究 <转>

    第一小节:HTTP Basics:使用Proxy软件(例如Webscarab)来截断浏览器(客户端)和Server之间的HTTP通信,之后任意篡改得到预期结果即可. 第二小节:HTTP Splitti ...

  8. CodeForces 706E Working routine

    十字链表. 开一个十字链表,矩阵中每一格作为一个节点,记录五个量: $s[i].L$:$i$节点左边的节点编号 $s[i].R$:$i$节点右边的节点编号 $s[i].U$:$i$节点上面的节点编号 ...

  9. 在Eclipse中用TODO标签管理任务(Task)

    Elipse为Java项目的时候,有一个很人性化的“任务管理”功能,利用这个功能可以方便地将项目中一些需要处理的任务记录下来.先来看看“任务管理”是怎么使用的吧.下面这个类在Class和name 属性 ...

  10. 编译搭建Lamp服务器

    Lamp 是目前倍受欢迎的一种网站服务器.其主要有linux+apache+mysql+php 组成.由于其组成成员都是开源免费的产品,所以被作为中小型网站服务器的选择.LZ之前在学校学linux的时 ...