贵阳网站设计zu97,网页游戏吃显卡还是cpu,运动网站开发的需求分析,免费开网店的一件代发数组与泛型相比#xff0c;有两个重要的不同点 。 首先#xff0c;数组是协变的#xff08; covariant #xff09; 。 这个词听起来有点吓人#xff0c;其实只是表示如果 Sub 为 Super 的子类型#xff0c;那么数组类型 Sub[ ]就是Super[ ]的子类型。 相反#xff0c;泛… 数组与泛型相比有两个重要的不同点 。 首先数组是协变的 covariant 。 这个词听起来有点吓人其实只是表示如果 Sub 为 Super 的子类型那么数组类型 Sub[ ]就是Super[ ]的子类型。 相反泛型则是可变的 invariant 对于任意两个不同的类型 Type1 和 Type2ListType1既不是 List Type2 的子类型也不是 ListType2 的超类型。 你可能认为这意味着泛型是有缺陷的但实际上可以说数组才是有缺陷的 。 下面的代码片段是合法的
Object[] objectArray new Long[1] ;
objectArray[0] I dont fit in;但下面这段代码则不合法
List0bject o1 new ArrayListLong();
o1.add(I dont fit in);这其中无论哪一种方法都不能将 String 放进 Long 容器中但是利用数组你会在运行时才发现所犯的错误而利用列表则可以在编译时就发现错误 。 我们当然希望在编译时就发现错误 。 数组与泛型之间的第二大区别在于数组是具体化的。因此数组会在运行时知道和强化它们的元素类型。如上所述如果企图将 String 保存到 Long 数组中就会得到一个 ArrayStoreException 异常 。 相比之下泛型则是通过擦除( erasure)来实现的 。 这意味着泛型只在编译时强化它们的类型信息并在运行时丢弃或者擦除它们的元素类型信息 。 擦除就是使泛型可以与没有使用泛型的代码随意进行互用以确保在 Java 5 中平滑过渡到泛型 。 由于上述这些根本的区别 因此数组和泛型不能很好地棍合使用 。 例如创建泛型、参数化类型或者类型参数的数组是非法的 3 这些数组创建表达式没有一个是合法的new ListE 、new ListString 和 new E 。 这些在编译时都会导致一个泛型数纽创建 generic array creation 错误 。 为什么创建泛型数组是非法的因为它不是类型安全的 。 要是它合法编译器在其他正确的程序中发生的转换就会在运行时失败并出现一个 ClassCastException 异常 。这就违背了泛型系统提供的基本保证 。 为了更具体地对此进行说明以下面的代码片段为例 我们假设第1行是合法的它创建了一个泛型数组 。 第 2 行创建并初始化了一个包含单个元素的 List Integer 。 第 3 行将 ListString数组保存到一个 Object 数组变量中这是合法的因为数组是协变的 。 第 4 行将 List Integer保存到 Object 数组里唯一的元素中这是可以的因为泛型是通过擦除实现的ListInteger 实例的运行时类型只是 List, ListString[ ] 实例的运行时类型则 是 List 因此这种安排不会产生 ArrayStoreExceptio口异常 。 但现在我们有麻烦了 。 我们将一个 ListInteger实例保存到了原本声明只包含 ListString实例的数组 中 。 在第 5 行中我们从这个数组里唯一的列表中获取了唯一的元素 。 编译器自动地将获取到 的元素转换成 String 但它是一个 Integer 因此我们在运行时得到了一个 ClassCastException 异常。 为了防止出现这种情况创建泛型数组的第1 行必须产生一条编译时错误 。 从技术的角度来说像 E 、ListE 和 ListString 这样的类型应称作不可具体化的 nonreifiable 类型。 直观地说不可具体化的 non-reifiable 类型是指其运行时表示法包含的信息 比它的 编译时表示法包含的信息更少的类型 。 唯一可具体化的( reifiable 参数化类型是无限制的通配符类型如 List 和 Map?,? 。虽然不常用但是创建无限制通配类型的数组是合法的 。 禁止创建泛型数组可能有点讨厌 。 例如这表明泛型一般不可能返回它的元素类型数组 。 这也意味着在结合使用可变参数 varargs 方法和泛型时会出现令人费解的警告 。 这是由于每当调用可变参数方法时就会创建一个数组来存放 varargs 参数 。 如果这个数组的元素类型不是可具体化的 reifialbe 就会得到一条警告 。 利用 Saf eVarargs 注解可以解决这个问题 。 当你得到泛型数组创建错误时最好的解决办法通常是优先使用集合类型 ListE,而不是数组类型 E[ ] 。 这样可能会损失一些性能或者简洁性但是换回的却是更高的类型安全性和互用性。 例如假设要通过构造器编写一个带有集合的 Chooser 类和一个方法并用该方法返回在集合中随机选择的一个元素 。 根据传给构造器的集合类型可以用 chooser 充当游戏用的色子、魔术 8 球一种卡片棋牌类游戏或者一个蒙特卡罗模拟的数据源 。 下面是一个没有使用泛型的简单实现
public class Chooser
{private final Object[] choiceArray;public Chooser(Collection choices) {choiceArray choices.toArray();}public object choose() {Random rnd ThreadLocalRandom.current();return choiceArray[rnd.nextInt(choiceArray.length)];}
} 要使用这个类必须将 choose 方法的返回值从 Object 转换成每次调用该方法时想要的类型如果搞错类型转换就会在运行时失败 。努力将Chooser 修改成泛型修改部分如下所示
public class ChooserT
{private final T[] choiceArray;public Chooser(CollectionT choices) {choiceArray choices.toArray();}// choose method unchanged
} 如果试着编译这个类将会得到以下错误消息 你可能会说 这没什么大不了的我可以把 Object 数组转换成 T 数组
choiceArray (T[]) choices.toArray() ;这样做的确消除了错误消息但是现在得到了一条警告 编译器告诉你它无法在运行时检查转换的安全性因为程序在运行时还不知道 T 是什么——记住元素类型信息会在运行时从泛型中被擦除 。 这段程序可以运行吗可以但是编译器无法证明这一点 。 你可以亲自证明只要将证据放在注释中用一条注解禁止警告但是最好能消除造成警告的根源 。 要消除未受检的转换警告必须选择用列表代替数组 。 下面是编译时没有出错或者警告的 Chooser 类版本
public class ChooserT {private final ListT choiceList;public Chooser(CollectionT choices) {choiceList new ArrayList (choices);}public T choose() {Random rnd ThreadLocalRandom.current();return choiceList.get(rnd.nextInt(choiceList.size());}
} 这个版本的代码稍微冗长 一点运行速度可能也会慢 一点 但是在运行时不会得到ClassCastException 异常为此也值了 。 总而言之数组和泛型有着截然不同的类型规则 。 数组是协变且可以具体化的泛型是不可变的且可以被擦除的 。 因此数组提供了运行时的类型安全但是没有编译时的类型安全反之对于泛型也一样 。 一般来说数组和泛型不能很好地混合使用 。 如果你发现自己将它们混合起来使用并且得到了编译时错误或者警告你的第一反应就应该是用列表代替数组 。