下面我总结了集合、泛型、数组转集合等一些常见的陷进,认真看完,相信你绝对有所收获。javascript
说实话,我敢保证不少人是不知道 List, List<?> 与 List<Object> 之间的区别的。java
一、咱们先来看看 List 与 List<Object>数组
不少可能以为 List<Object>的用法与 List 是同样的,例如不少人认为安全
List<Object> list;markdown
与async
List list;ide
这两种定义方法是如出一辙的,然而他们是不同的。看下面一段代码post
List<Integer> t1 = new ArrayList<>(); // 编译经过 List t2 = t1; //编译失败 List<Object> t3 = t1;
t1 能够赋给 t2, 可是 t1 不能赋给 t3,会抛出以下异常ui
从这里能够看出google
List list;
与
List<Object> list;
是有区别的,List 变量能够接受任何泛型的变量,而 List 则不能够。 二、咱们在看看 Lis<?> 有什么须要注意的地方: 看下面一段代码: List<Object> t1 = new ArrayList<>(); List<?> t2 = t1; // 编译经过 t2.remove(0); t2.clear(); // 编译不经过 t2.add(new Object()); List<?> 是一个泛型,在没有赋值以前,是能够接受任何集合的赋值的,我想这点你们都知道,可是请注意,赋值以后就不能往里面添加元素了,提示以下错误: 因此 List<?> 通常用来做为参数来接受外部的集合,或者返回一个不知道具体元素的集合。 List 与 List<?>, List<Object> 的细微区别知道了吧? 二、<? extends T> 与 <? super T>你真的懂吗? 咱们知道泛型 List<T> 只能放置一种类型,若是你采用 List<Object> 来放置多种类型,而后再进行类型强制转换的话,那会失去了泛型的初衷。 为了可以放置多种类型,因而有了 <? extend T> 与 <? super T>,下面先说一些你可能本来就知道的知识: 一、对于 <? extends T> a,a 这个变量能够接受 T 及其 T 子类的集合,上界为 T,而且从 a 取出来的类型都会被强制转换为 T。重点看下面一个例子: 注意:咱们先约定 Cat(猫) 继承自 Animal(动物),RedCat(黑猫) 继承自 Cat List<Animal> animals = new ArrayList<>(); List<Cat> cats = new ArrayList<>(); List<RedCat> redCats = new ArrayList<>(); // 能够经过编译 List<? extends Cat> extendsCat = redCats; // 不能经过编译,由于只能接受 Cat 及其子类的集合 extendsCat = animals; // 重点注意:下面三行都不能经过编译 extendsCat.add(new Animal()); extendsCat.add(new Cat()); extendsCat.add(new RedCat()); // 重点注意:能够经过编译 extendsCat.add(null); 注意,<? extends T>最须要注意的是,就是不能向里面添加除null以外的其余全部元素,这个和 List<?> 有点相似。 二、如今说说 <? super T>,它和 <? extends T> 有点相反。对于 <? super T> a,a 这个变量能够接受 T 及其 T 父类的集合,下界为 T,而且从 a 取出来的类型都会被强制转换为 Object。重点看下面一个例子: List<Animal> animals = new ArrayList<>(); List<Cat> cats = new ArrayList<>(); List<RedCat> redCats = new ArrayList<>(); // 能够经过编译 List<? super Cat> superCat = animals; // 不能经过编译,由于只能接受 Cat 及其父类的集合 superCat = redCats; // 重点注意:不能经过编译,只能添加 Cat 及其 Cat 的子类 superCat.add(new Animal()); // 重点注意,能够经过编译 superCat.add(new Cat()); superCat.add(new RedCat()); superCat.add(null); 注意,<? super T>最须要注意的是,在虽然能够接受 T 及其父类的赋值,可是只能向里面添加 T 及其 T 的子类。 总结 一、List<? extends T> a ,能够把 a 及其 a 的子类赋给 a,从 a 里取的元素都会被强制转换为 T 类型,不过须要注意的是,不能向 a 添加任何除 null 外是元素。 二、List<? super T> a ,能够把 a 及其 a 的父类赋给 a,从 a 里取的元素都会被强制转换为 Object 类型,不过须要注意的是,能够向 a 添加元素,但添加的只能是 T 及其子类元素。 三、泛型与重载 咱们先来看一道题,你以为下面这道题可以编译经过吗? public class GernerTypes { public static void method(List<Integer> list) { System.out.println("List<Integer> list"); } public static void method(List<String> list) { System.out.println("List<String> list"); } } 答是编译不经过。 两个方法的参数不一样,为何会重载不经过呢? 实际上在 Java 的泛型中,泛型只存在于源码中,在编译后的字节码中,泛型已经被替换为原生类型了,而且在相应的地方插入了强制转换的代码。为了方便理解,能够看下面的一段代码例子: // 源码 public static void main(String[] args) { List<Integer> list = new ArrayList<>(); list.add(1); System.out.println(list.get(0)); } 编译以后泛型就不存在了,而且在相应的地方插入了强制转换的代码,编译以后,咱们反编译的代码以下: // 反编译以后的代码 public static void main(String[] args) { List list = new ArrayList(); list.add(1); System.out.println((Integer)list.get(0)); } 这种 编译以后泛型就不存在了,而且在相应的地方插入了强制转换代码的机制咱们也称之为擦除。 因此上面的两个方法,看似参数不同,可是通过编译擦出以后,他们的参数就是同样的了,因此编译不经过。 四、数组与集合相互转换时须要注意的点 一、数组转集合 你们先看一个例子吧, public static void main(String[] args) { String[] arr = {"one", "two", "three"}; // 数组转换成集合 List<String> list = Arrays.asList(arr); // 向集合添加元素:编译正常,但运行时抛出了异常 list.add("four"); } 向集合添加元素抛出了以下异常: 问题来了,向集合添加元素为啥会抛出异常呢?? 咱们先来看一下 Arrays.asList(arr) 方法究竟返回了什么? 源码以下: 返回的明明是 ArrayList 啊,为啥就不能添加元素呢?? 实际上,此 ArrayList 非彼 ArrayList,这个返回的 ArrayList 其实是 Arrays 的一个内部类。该内部类也是十分简单,和真实的那个 ArrayList 没得比,部分源码以下: 并且这个假的 ArrayList 是直接 引用原数组的,否则你看它的构造器(第二条画线) 。 也就是说,ArrayList 内部是直接引用 arr 数组,你对 arr 数组进行改变,也会同时改变到 list 集合。 下面的代码证实这一点 public static void main(String[] args) { String[] arr = {"one", "two", "three"}; // 数组转换成集合 List<String> list = Arrays.asList(arr); // 修改 arr arr[0] = "0"; //打印看看 System.out.println(list.get(0)); } 打印结果是 “0”。 因此,咱们向 list 添加元素确定失败,由于 arr 数组的长度了 3 ,原本就有 3 个元素了,你在向里面添加第四个元素,确定是不行的。 因此,在把数组转换为集合的过程当中,须要特别注意。 建议你们这样转换比较安全 List<String> list = new ArrayList<>(Arrays.asList(arr)); 二、集合转数组 集合转换为数组相对比较不苛刻,我就不拉不少源码来进行分析了,我只简单说下几个须要注意的地方。例如对于下面这个转换: // 集合大小为 size List<String> list = new ArrayList<>(); // 长度为 n 的数组 String[] arr = new String[n]; // 进行转换 list.toArray(arr); 一、若是数组长度比集合小:因为 arr 的长度不够,因此集合里的元素不会赋给 arr,并且本身再从新建立一个新数组反回去。 二、若是数组长度不小于集合:此时 arr 的长度够了,因此集合里的元素直接复制给 arr 数组,不会从新建立一个新的元素。 一览源码: public <T> T[] toArray(T[] a) { if (a.length < size) // 从新建立一个数组来返回去 return (T[]) Arrays.copyOf(elementData, size, a.getClass()); // 长度够的话直接复制给 a System.arraycopy(elementData, 0, a, 0, size); if (a.length > size) a[size] = null; return a; } 以上这些陷进相信有很多人是不知道了,我把它总结整理了出来,但愿你们看完可以有所收获。 下面我总结了集合、泛型、数组转集合等一些常见的陷进,认真看完,相信你绝对有所收获。 一、List ,List<?> 与 List<Object> 有区别吗? 说实话,我敢保证不少人是不知道 List, List<?> 与 List<Object> 之间的区别的。 一、咱们先来看看 List 与 List<Object> 不少可能以为 List<Object>的用法与 List 是同样的,例如不少人认为 List<Object> list; 与 List list; 这两种定义方法是如出一辙的,然而他们是不同的。看下面一段代码 List<Integer> t1 = new ArrayList<>(); // 编译经过 List t2 = t1; //编译失败 List<Object> t3 = t1; t1 能够赋给 t2, 可是 t1 不能赋给 t3,会抛出以下异常 从这里能够看出 List list; 与 List<Object> list; 是有区别的,List 变量能够接受任何泛型的变量,而 List 则不能够。 二、咱们在看看 Lis<?> 有什么须要注意的地方: 看下面一段代码: List<Object> t1 = new ArrayList<>(); List<?> t2 = t1; // 编译经过 t2.remove(0); t2.clear(); // 编译不经过 t2.add(new Object()); List<?> 是一个泛型,在没有赋值以前,是能够接受任何集合的赋值的,我想这点你们都知道,可是请注意,赋值以后就不能往里面添加元素了,提示以下错误: 因此 List<?> 通常用来做为参数来接受外部的集合,或者返回一个不知道具体元素的集合。 List 与 List<?>, List<Object> 的细微区别知道了吧? 二、<? extends T> 与 <? super T>你真的懂吗? 咱们知道泛型 List<T> 只能放置一种类型,若是你采用 List<Object> 来放置多种类型,而后再进行类型强制转换的话,那会失去了泛型的初衷。 为了可以放置多种类型,因而有了 <? extend T> 与 <? super T>,下面先说一些你可能本来就知道的知识: 一、对于 <? extends T> a,a 这个变量能够接受 T 及其 T 子类的集合,上界为 T,而且从 a 取出来的类型都会被强制转换为 T。重点看下面一个例子: 注意:咱们先约定 Cat(猫) 继承自 Animal(动物),RedCat(黑猫) 继承自 Cat List<Animal> animals = new ArrayList<>(); List<Cat> cats = new ArrayList<>(); List<RedCat> redCats = new ArrayList<>(); // 能够经过编译 List<? extends Cat> extendsCat = redCats; // 不能经过编译,由于只能接受 Cat 及其子类的集合 extendsCat = animals; // 重点注意:下面三行都不能经过编译 extendsCat.add(new Animal()); extendsCat.add(new Cat()); extendsCat.add(new RedCat()); // 重点注意:能够经过编译 extendsCat.add(null); 注意,<? extends T>最须要注意的是,就是不能向里面添加除null以外的其余全部元素,这个和 List<?> 有点相似。 二、如今说说 <? super T>,它和 <? extends T> 有点相反。对于 <? super T> a,a 这个变量能够接受 T 及其 T 父类的集合,下界为 T,而且从 a 取出来的类型都会被强制转换为 Object。重点看下面一个例子: List<Animal> animals = new ArrayList<>(); List<Cat> cats = new ArrayList<>(); List<RedCat> redCats = new ArrayList<>(); // 能够经过编译 List<? super Cat> superCat = animals; // 不能经过编译,由于只能接受 Cat 及其父类的集合 superCat = redCats; // 重点注意:不能经过编译,只能添加 Cat 及其 Cat 的子类 superCat.add(new Animal()); // 重点注意,能够经过编译 superCat.add(new Cat()); superCat.add(new RedCat()); superCat.add(null); 注意,<? super T>最须要注意的是,在虽然能够接受 T 及其父类的赋值,可是只能向里面添加 T 及其 T 的子类。 总结 一、List<? extends T> a ,能够把 a 及其 a 的子类赋给 a,从 a 里取的元素都会被强制转换为 T 类型,不过须要注意的是,不能向 a 添加任何除 null 外是元素。 二、List<? super T> a ,能够把 a 及其 a 的父类赋给 a,从 a 里取的元素都会被强制转换为 Object 类型,不过须要注意的是,能够向 a 添加元素,但添加的只能是 T 及其子类元素。 三、泛型与重载 咱们先来看一道题,你以为下面这道题可以编译经过吗? public class GernerTypes { public static void method(List<Integer> list) { System.out.println("List<Integer> list"); } public static void method(List<String> list) { System.out.println("List<String> list"); } } 答是编译不经过。 两个方法的参数不一样,为何会重载不经过呢? 实际上在 Java 的泛型中,泛型只存在于源码中,在编译后的字节码中,泛型已经被替换为原生类型了,而且在相应的地方插入了强制转换的代码。为了方便理解,能够看下面的一段代码例子: // 源码 public static void main(String[] args) { List<Integer> list = new ArrayList<>(); list.add(1); System.out.println(list.get(0)); } 编译以后泛型就不存在了,而且在相应的地方插入了强制转换的代码,编译以后,咱们反编译的代码以下: // 反编译以后的代码 public static void main(String[] args) { List list = new ArrayList(); list.add(1); System.out.println((Integer)list.get(0)); } 这种 编译以后泛型就不存在了,而且在相应的地方插入了强制转换代码的机制咱们也称之为擦除。 因此上面的两个方法,看似参数不同,可是通过编译擦出以后,他们的参数就是同样的了,因此编译不经过。 四、数组与集合相互转换时须要注意的点 一、数组转集合 你们先看一个例子吧, public static void main(String[] args) { String[] arr = {"one", "two", "three"}; // 数组转换成集合 List<String> list = Arrays.asList(arr); // 向集合添加元素:编译正常,但运行时抛出了异常 list.add("four"); } 向集合添加元素抛出了以下异常: 问题来了,向集合添加元素为啥会抛出异常呢?? 咱们先来看一下 Arrays.asList(arr) 方法究竟返回了什么? 源码以下: 返回的明明是 ArrayList 啊,为啥就不能添加元素呢?? 实际上,此 ArrayList 非彼 ArrayList,这个返回的 ArrayList 其实是 Arrays 的一个内部类。该内部类也是十分简单,和真实的那个 ArrayList 没得比,部分源码以下: 并且这个假的 ArrayList 是直接 引用原数组的,否则你看它的构造器(第二条画线) 。 也就是说,ArrayList 内部是直接引用 arr 数组,你对 arr 数组进行改变,也会同时改变到 list 集合。 下面的代码证实这一点 public static void main(String[] args) { String[] arr = {"one", "two", "three"}; // 数组转换成集合 List<String> list = Arrays.asList(arr); // 修改 arr arr[0] = "0"; //打印看看 System.out.println(list.get(0)); } 打印结果是 “0”。 因此,咱们向 list 添加元素确定失败,由于 arr 数组的长度了 3 ,原本就有 3 个元素了,你在向里面添加第四个元素,确定是不行的。 因此,在把数组转换为集合的过程当中,须要特别注意。 建议你们这样转换比较安全 List<String> list = new ArrayList<>(Arrays.asList(arr)); 二、集合转数组 集合转换为数组相对比较不苛刻,我就不拉不少源码来进行分析了,我只简单说下几个须要注意的地方。例如对于下面这个转换: // 集合大小为 size List<String> list = new ArrayList<>(); // 长度为 n 的数组 String[] arr = new String[n]; // 进行转换 list.toArray(arr); 一、若是数组长度比集合小:因为 arr 的长度不够,因此集合里的元素不会赋给 arr,并且本身再从新建立一个新数组反回去。 二、若是数组长度不小于集合:此时 arr 的长度够了,因此集合里的元素直接复制给 arr 数组,不会从新建立一个新的元素。 一览源码: public <T> T[] toArray(T[] a) { if (a.length < size) // 从新建立一个数组来返回去 return (T[]) Arrays.copyOf(elementData, size, a.getClass()); // 长度够的话直接复制给 a System.arraycopy(elementData, 0, a, 0, size); if (a.length > size) a[size] = null; return a; } 以上这些陷进相信有很多人是不知道了,我把它总结整理了出来,若是你们看完以为有收获,不妨点个底部小卡片 + 点赞鼓励我一下? posted @ 2019-03-01 16:47 帅地 阅读( ...) 评论( ...) 编辑 收藏 刷新评论 刷新页面 返回顶部
二、咱们在看看 Lis<?> 有什么须要注意的地方:
看下面一段代码:
List<Object> t1 = new ArrayList<>(); List<?> t2 = t1; // 编译经过 t2.remove(0); t2.clear(); // 编译不经过 t2.add(new Object());
List<?> 是一个泛型,在没有赋值以前,是能够接受任何集合的赋值的,我想这点你们都知道,可是请注意,赋值以后就不能往里面添加元素了,提示以下错误:
因此 List<?> 通常用来做为参数来接受外部的集合,或者返回一个不知道具体元素的集合。
List 与 List<?>, List<Object> 的细微区别知道了吧?
咱们知道泛型 List<T> 只能放置一种类型,若是你采用 List<Object> 来放置多种类型,而后再进行类型强制转换的话,那会失去了泛型的初衷。
为了可以放置多种类型,因而有了 <? extend T> 与 <? super T>,下面先说一些你可能本来就知道的知识:
一、对于 <? extends T> a,a 这个变量能够接受 T 及其 T 子类的集合,上界为 T,而且从 a 取出来的类型都会被强制转换为 T。重点看下面一个例子:
注意:咱们先约定 Cat(猫) 继承自 Animal(动物),RedCat(黑猫) 继承自 Cat
List<Animal> animals = new ArrayList<>(); List<Cat> cats = new ArrayList<>(); List<RedCat> redCats = new ArrayList<>(); // 能够经过编译 List<? extends Cat> extendsCat = redCats; // 不能经过编译,由于只能接受 Cat 及其子类的集合 extendsCat = animals; // 重点注意:下面三行都不能经过编译 extendsCat.add(new Animal()); extendsCat.add(new Cat()); extendsCat.add(new RedCat()); // 重点注意:能够经过编译 extendsCat.add(null);
注意,<? extends T>最须要注意的是,就是不能向里面添加除null以外的其余全部元素,这个和 List<?> 有点相似。
二、如今说说 <? super T>,它和 <? extends T> 有点相反。对于 <? super T> a,a 这个变量能够接受 T 及其 T 父类的集合,下界为 T,而且从 a 取出来的类型都会被强制转换为 Object。重点看下面一个例子:
List<Animal> animals = new ArrayList<>(); List<Cat> cats = new ArrayList<>(); List<RedCat> redCats = new ArrayList<>(); // 能够经过编译 List<? super Cat> superCat = animals; // 不能经过编译,由于只能接受 Cat 及其父类的集合 superCat = redCats; // 重点注意:不能经过编译,只能添加 Cat 及其 Cat 的子类 superCat.add(new Animal()); // 重点注意,能够经过编译 superCat.add(new Cat()); superCat.add(new RedCat()); superCat.add(null);
注意,<? super T>最须要注意的是,在虽然能够接受 T 及其父类的赋值,可是只能向里面添加 T 及其 T 的子类。
总结
一、List<? extends T> a ,能够把 a 及其 a 的子类赋给 a,从 a 里取的元素都会被强制转换为 T 类型,不过须要注意的是,不能向 a 添加任何除 null 外是元素。
二、List<? super T> a ,能够把 a 及其 a 的父类赋给 a,从 a 里取的元素都会被强制转换为 Object 类型,不过须要注意的是,能够向 a 添加元素,但添加的只能是 T 及其子类元素。
咱们先来看一道题,你以为下面这道题可以编译经过吗?
public class GernerTypes { public static void method(List<Integer> list) { System.out.println("List<Integer> list"); } public static void method(List<String> list) { System.out.println("List<String> list"); } }
答是编译不经过。
两个方法的参数不一样,为何会重载不经过呢?
实际上在 Java 的泛型中,泛型只存在于源码中,在编译后的字节码中,泛型已经被替换为原生类型了,而且在相应的地方插入了强制转换的代码。为了方便理解,能够看下面的一段代码例子:
// 源码 public static void main(String[] args) { List<Integer> list = new ArrayList<>(); list.add(1); System.out.println(list.get(0)); }
编译以后泛型就不存在了,而且在相应的地方插入了强制转换的代码,编译以后,咱们反编译的代码以下:
// 反编译以后的代码 public static void main(String[] args) { List list = new ArrayList(); list.add(1); System.out.println((Integer)list.get(0)); }
这种 编译以后泛型就不存在了,而且在相应的地方插入了强制转换代码的机制咱们也称之为擦除。
因此上面的两个方法,看似参数不同,可是通过编译擦出以后,他们的参数就是同样的了,因此编译不经过。
一、数组转集合
你们先看一个例子吧,
public static void main(String[] args) { String[] arr = {"one", "two", "three"}; // 数组转换成集合 List<String> list = Arrays.asList(arr); // 向集合添加元素:编译正常,但运行时抛出了异常 list.add("four"); }
向集合添加元素抛出了以下异常:
问题来了,向集合添加元素为啥会抛出异常呢??
咱们先来看一下 Arrays.asList(arr) 方法究竟返回了什么?
源码以下:
返回的明明是 ArrayList 啊,为啥就不能添加元素呢??
实际上,此 ArrayList 非彼 ArrayList,这个返回的 ArrayList 其实是 Arrays 的一个内部类。该内部类也是十分简单,和真实的那个 ArrayList 没得比,部分源码以下:
并且这个假的 ArrayList 是直接 引用原数组的,否则你看它的构造器(第二条画线)
。
也就是说,ArrayList 内部是直接引用 arr 数组,你对 arr 数组进行改变,也会同时改变到 list 集合。
下面的代码证实这一点
public static void main(String[] args) { String[] arr = {"one", "two", "three"}; // 数组转换成集合 List<String> list = Arrays.asList(arr); // 修改 arr arr[0] = "0"; //打印看看 System.out.println(list.get(0)); }
打印结果是 “0”。
因此,咱们向 list 添加元素确定失败,由于 arr 数组的长度了 3 ,原本就有 3 个元素了,你在向里面添加第四个元素,确定是不行的。
因此,在把数组转换为集合的过程当中,须要特别注意。
建议你们这样转换比较安全
List<String> list = new ArrayList<>(Arrays.asList(arr));
二、集合转数组
集合转换为数组相对比较不苛刻,我就不拉不少源码来进行分析了,我只简单说下几个须要注意的地方。例如对于下面这个转换:
// 集合大小为 size List<String> list = new ArrayList<>(); // 长度为 n 的数组 String[] arr = new String[n]; // 进行转换 list.toArray(arr);
一、若是数组长度比集合小:因为 arr 的长度不够,因此集合里的元素不会赋给 arr,并且本身再从新建立一个新数组反回去。
二、若是数组长度不小于集合:此时 arr 的长度够了,因此集合里的元素直接复制给 arr 数组,不会从新建立一个新的元素。
一览源码:
public <T> T[] toArray(T[] a) { if (a.length < size) // 从新建立一个数组来返回去 return (T[]) Arrays.copyOf(elementData, size, a.getClass()); // 长度够的话直接复制给 a System.arraycopy(elementData, 0, a, 0, size); if (a.length > size) a[size] = null; return a; }
以上这些陷进相信有很多人是不知道了,我把它总结整理了出来,但愿你们看完可以有所收获。
下面我总结了集合、泛型、数组转集合等一些常见的陷进,认真看完,相信你绝对有所收获。
说实话,我敢保证不少人是不知道 List, List<?> 与 List<Object> 之间的区别的。
一、咱们先来看看 List 与 List<Object>
不少可能以为 List<Object>的用法与 List 是同样的,例如不少人认为
这两种定义方法是如出一辙的,然而他们是不同的。看下面一段代码
t1 能够赋给 t2, 可是 t1 不能赋给 t3,会抛出以下异常
从这里能够看出
是有区别的,List 变量能够接受任何泛型的变量,而 List 则不能够。 二、咱们在看看 Lis<?> 有什么须要注意的地方: 看下面一段代码: List<Object> t1 = new ArrayList<>(); List<?> t2 = t1; // 编译经过 t2.remove(0); t2.clear(); // 编译不经过 t2.add(new Object()); List<?> 是一个泛型,在没有赋值以前,是能够接受任何集合的赋值的,我想这点你们都知道,可是请注意,赋值以后就不能往里面添加元素了,提示以下错误: 因此 List<?> 通常用来做为参数来接受外部的集合,或者返回一个不知道具体元素的集合。 List 与 List<?>, List<Object> 的细微区别知道了吧? 二、<? extends T> 与 <? super T>你真的懂吗? 咱们知道泛型 List<T> 只能放置一种类型,若是你采用 List<Object> 来放置多种类型,而后再进行类型强制转换的话,那会失去了泛型的初衷。 为了可以放置多种类型,因而有了 <? extend T> 与 <? super T>,下面先说一些你可能本来就知道的知识: 一、对于 <? extends T> a,a 这个变量能够接受 T 及其 T 子类的集合,上界为 T,而且从 a 取出来的类型都会被强制转换为 T。重点看下面一个例子: 注意:咱们先约定 Cat(猫) 继承自 Animal(动物),RedCat(黑猫) 继承自 Cat List<Animal> animals = new ArrayList<>(); List<Cat> cats = new ArrayList<>(); List<RedCat> redCats = new ArrayList<>(); // 能够经过编译 List<? extends Cat> extendsCat = redCats; // 不能经过编译,由于只能接受 Cat 及其子类的集合 extendsCat = animals; // 重点注意:下面三行都不能经过编译 extendsCat.add(new Animal()); extendsCat.add(new Cat()); extendsCat.add(new RedCat()); // 重点注意:能够经过编译 extendsCat.add(null); 注意,<? extends T>最须要注意的是,就是不能向里面添加除null以外的其余全部元素,这个和 List<?> 有点相似。 二、如今说说 <? super T>,它和 <? extends T> 有点相反。对于 <? super T> a,a 这个变量能够接受 T 及其 T 父类的集合,下界为 T,而且从 a 取出来的类型都会被强制转换为 Object。重点看下面一个例子: List<Animal> animals = new ArrayList<>(); List<Cat> cats = new ArrayList<>(); List<RedCat> redCats = new ArrayList<>(); // 能够经过编译 List<? super Cat> superCat = animals; // 不能经过编译,由于只能接受 Cat 及其父类的集合 superCat = redCats; // 重点注意:不能经过编译,只能添加 Cat 及其 Cat 的子类 superCat.add(new Animal()); // 重点注意,能够经过编译 superCat.add(new Cat()); superCat.add(new RedCat()); superCat.add(null); 注意,<? super T>最须要注意的是,在虽然能够接受 T 及其父类的赋值,可是只能向里面添加 T 及其 T 的子类。 总结 一、List<? extends T> a ,能够把 a 及其 a 的子类赋给 a,从 a 里取的元素都会被强制转换为 T 类型,不过须要注意的是,不能向 a 添加任何除 null 外是元素。 二、List<? super T> a ,能够把 a 及其 a 的父类赋给 a,从 a 里取的元素都会被强制转换为 Object 类型,不过须要注意的是,能够向 a 添加元素,但添加的只能是 T 及其子类元素。 三、泛型与重载 咱们先来看一道题,你以为下面这道题可以编译经过吗? public class GernerTypes { public static void method(List<Integer> list) { System.out.println("List<Integer> list"); } public static void method(List<String> list) { System.out.println("List<String> list"); } } 答是编译不经过。 两个方法的参数不一样,为何会重载不经过呢? 实际上在 Java 的泛型中,泛型只存在于源码中,在编译后的字节码中,泛型已经被替换为原生类型了,而且在相应的地方插入了强制转换的代码。为了方便理解,能够看下面的一段代码例子: // 源码 public static void main(String[] args) { List<Integer> list = new ArrayList<>(); list.add(1); System.out.println(list.get(0)); } 编译以后泛型就不存在了,而且在相应的地方插入了强制转换的代码,编译以后,咱们反编译的代码以下: // 反编译以后的代码 public static void main(String[] args) { List list = new ArrayList(); list.add(1); System.out.println((Integer)list.get(0)); } 这种 编译以后泛型就不存在了,而且在相应的地方插入了强制转换代码的机制咱们也称之为擦除。 因此上面的两个方法,看似参数不同,可是通过编译擦出以后,他们的参数就是同样的了,因此编译不经过。 四、数组与集合相互转换时须要注意的点 一、数组转集合 你们先看一个例子吧, public static void main(String[] args) { String[] arr = {"one", "two", "three"}; // 数组转换成集合 List<String> list = Arrays.asList(arr); // 向集合添加元素:编译正常,但运行时抛出了异常 list.add("four"); } 向集合添加元素抛出了以下异常: 问题来了,向集合添加元素为啥会抛出异常呢?? 咱们先来看一下 Arrays.asList(arr) 方法究竟返回了什么? 源码以下: 返回的明明是 ArrayList 啊,为啥就不能添加元素呢?? 实际上,此 ArrayList 非彼 ArrayList,这个返回的 ArrayList 其实是 Arrays 的一个内部类。该内部类也是十分简单,和真实的那个 ArrayList 没得比,部分源码以下: 并且这个假的 ArrayList 是直接 引用原数组的,否则你看它的构造器(第二条画线) 。 也就是说,ArrayList 内部是直接引用 arr 数组,你对 arr 数组进行改变,也会同时改变到 list 集合。 下面的代码证实这一点 public static void main(String[] args) { String[] arr = {"one", "two", "three"}; // 数组转换成集合 List<String> list = Arrays.asList(arr); // 修改 arr arr[0] = "0"; //打印看看 System.out.println(list.get(0)); } 打印结果是 “0”。 因此,咱们向 list 添加元素确定失败,由于 arr 数组的长度了 3 ,原本就有 3 个元素了,你在向里面添加第四个元素,确定是不行的。 因此,在把数组转换为集合的过程当中,须要特别注意。 建议你们这样转换比较安全 List<String> list = new ArrayList<>(Arrays.asList(arr)); 二、集合转数组 集合转换为数组相对比较不苛刻,我就不拉不少源码来进行分析了,我只简单说下几个须要注意的地方。例如对于下面这个转换: // 集合大小为 size List<String> list = new ArrayList<>(); // 长度为 n 的数组 String[] arr = new String[n]; // 进行转换 list.toArray(arr); 一、若是数组长度比集合小:因为 arr 的长度不够,因此集合里的元素不会赋给 arr,并且本身再从新建立一个新数组反回去。 二、若是数组长度不小于集合:此时 arr 的长度够了,因此集合里的元素直接复制给 arr 数组,不会从新建立一个新的元素。 一览源码: public <T> T[] toArray(T[] a) { if (a.length < size) // 从新建立一个数组来返回去 return (T[]) Arrays.copyOf(elementData, size, a.getClass()); // 长度够的话直接复制给 a System.arraycopy(elementData, 0, a, 0, size); if (a.length > size) a[size] = null; return a; } 以上这些陷进相信有很多人是不知道了,我把它总结整理了出来,若是你们看完以为有收获,不妨点个底部小卡片 + 点赞鼓励我一下? posted @ 2019-03-01 16:47 帅地 阅读( ...) 评论( ...) 编辑 收藏 刷新评论 刷新页面 返回顶部
以上这些陷进相信有很多人是不知道了,我把它总结整理了出来,若是你们看完以为有收获,不妨点个底部小卡片 + 点赞鼓励我一下?