12-泛型

12—泛型

java 泛型详解

1. 为什么要有泛型

1.1 泛型(Generic)
  • 泛型:标签

  • 举例:

    • 中药店,每个抽屉外面贴着标签
    • 超市购物架上很多瓶子,每个瓶子装的是什么,有标签
  • 泛型的设计背景

    结合容器在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在JDK5.0之前只能把元素类型设计为Object,JDK1.5之后使用泛型解决。因为这个时候除了元素的类型不确定,其他的部分是确定的,例如这个元素如何保存,如何管理等是确定的,因此此时把元素的类型设计成一个参数,这个类型参数叫做泛型。Collection,List,ArrayList这个就是类型参数,即泛型。

1.2 泛型的概念
  • 所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值类型及参数类型。这个类型参数将在使用时(例如:继承或实现这个接口,用这个类型声明变量、创建对象时)确定(即传入实际的类型参数,也称为类型参数)
  • 从JDK1.5以后,Java引入了”参数化类型(Parameteized type)“的概念,允许我们在创建集合时再指定集合元素的类型,正如:List,这表明该List只能保存字符串类型的对象
  • JDK1.5改写了集合框架中的全部接口和类,为这些接口、类增加了泛型支持,从而可以在声明集合变量、创建集合对象时传入类型实参

为什么要有泛型呢,直接Object不是也可以存储数据吗?

  1. 解决元素存储的安全性问题,好比商品、药品标签,不会弄错

  2. 解决获取元素时,需要类型强制转换的问题,好比不用每次拿商品、药品都要辨别

  3. 在集合没有泛型时

    image-20210728175558063

  4. 在集合有泛型时

    image-20210728175639031

Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常。同时,代码更加简洁、健壮

1.3 概述
1
2
3
泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。

泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。
1.4 一个例子
1
2
3
4
5
6
7
8
List arrayList = new ArrayList();
arrayList.add("aaaa");
arrayList.add(100);

for(int i = 0; i< arrayList.size();i++){
String item = (String)arrayList.get(i);
Log.d("泛型测试","item = " + item);
}

程序崩溃

java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

ArrayList可以存放任意类型,例子中添加了一个String类型,添加了一个Integer类型,再使用时都以String的方式使用,因此程序崩溃了。为了解决类似这样的问题(在编译阶段就可以解决),泛型应运而生。我们将第一行声明初始化list的代码更改一下,编译器会在编译阶段就能够帮我们发现类似这样的问题。

1
2
3
List<String> arrayList = new ArrayList<String>();
...
//arrayList.add(100); 在编译阶段,编译器就会报错
1.5 特性

泛型只在编译阶段有效

1
2
3
4
5
6
7
8
9
List<String> stringArrayList = new ArrayList<String>();
List<Integer> integerArrayList = new ArrayList<Integer>();

Class classStringArrayList = stringArrayList.getClass();
Class classIntegerArrayList = integerArrayList.getClass();

if(classStringArrayList.equals(classIntegerArrayList)){
Log.d("泛型测试","类型相同");
}

D/泛型测试: 类型相同

通过上面的例子可以证明,在编译之后程序会采取去泛型化的措施。也就是说Java中的泛型,只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段。

对此总结成一句话:泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型。

2. 自定义泛型结构

2.1 自定义泛型结构
  1. 泛型的声明

    interface List 和calss GenTest<K,V>,其中,T,K,V不代表值,而是表示类型。这里使用任意字母都可以。常用T表示,是Type的缩写

  2. 泛型的实例化

    一定要在类名后面指定类型参数的值(类型)。如:

    List strList = new ArrayList();

    Iterator iterator = customers.iterator();

    • T只能是类,不能用基本数据类型填充。但可以使用包装类填充
    • 把一个集合中的内容限制为一个特定的数据类型,这就是generics背后的核心思想
    • image-20210729212121077
2.2 泛型类、泛型接口
  1. 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:<E1,E2,E3>

  2. 泛型类的构造器如下:public GenericClass(){}

    而下面是错误的:public GenericClass(){}

  3. 实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致

  4. 泛型不同的引用不能相互赋值

    尽管在编译时ArrayList和ArrayList是两种类型,但是,在运行时只有一个ArrayList被加载到JVM

  5. 泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,且不等价于Object。经验:泛型要使用一路都用。要不用,一路都不要用

  6. 如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象

  7. JDK1.7,泛型的简化操作:ArrayList firt = new ArrayList<>();

  8. 泛型的指定中不能使用基本数据类型,可以使用包装类替换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class GenericTest {
public static void main(String[] args) {
// 1、使用时:类似于Object,不等同于Object
ArrayList list = new ArrayList();
// list.add(new Date());//有风险
list.add("hello");
test(list);// 泛型擦除,编译不会类型检查
// ArrayList<Object> list2 = new ArrayList<Object>();
// test(list2);//一旦指定Object,编译会类型检查,必须按照Object处理
}
public static void test(ArrayList<String> list) {
String str = "";
for (String s : list) {
str += s + ",";
}
System.out.println("元素:" + str);
}
}
  1. 在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态 属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法 中不能使用类的泛型。

  2. 异常类不能是泛型的

  3. 不能使用new E[]。但是可以:E[] elements = (E[])new Object[capacity]; 参考:ArrayList源码中声明:Object[] elementData,而非泛型参数类型数组。

  4. 父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型:

    • 子类不保留父类的泛型:按需实现
      • 没有类型 擦除
      • 具体类型
    • 子类保留父类的泛型:泛型子类
      • 全部保留
      • 部分保留

    结论:子类必须是“富二代”,子类除了指定或保留父类的泛型,还可以增加自 己的泛型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Father<T1, T2> {
}
// 子类不保留父类的泛型
// 1)没有类型 擦除
class Son1 extends Father {// 等价于class Son extends Father<Object,Object>{
}
// 2)具体类型
class Son2 extends Father<Integer, String> {
}
// 子类保留父类的泛型
// 1)全部保留
class Son3<T1, T2> extends Father<T1, T2> {
}
// 2)部分保留
class Son4<T2> extends Father<Integer, T2> {
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Father<T1, T2> {
}
// 子类不保留父类的泛型
// 1)没有类型 擦除
class Son<A, B> extends Father{//等价于class Son extends Father<Object,Object>{
}
// 2)具体类型
class Son2<A, B> extends Father<Integer, String> {
}
// 子类保留父类的泛型
// 1)全部保留
class Son3<T1, T2, A, B> extends Father<T1, T2> {
}
// 2)部分保留
class Son4<T2, A, B> extends Father<Integer, T2> {
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Person<T> {
// 使用T类型定义变量
private T info;
// 使用T类型定义一般方法
public T getInfo() {
return info;
}
public void setInfo(T info) {
this.info = info;
}
// 使用T类型定义构造器
public Person() {
}
public Person(T info) {
this.info = info;
}
  • 方法,也可以被泛型化,不管此时定义在其中的类是不是泛型类。在泛型 方法中可以定义泛型参数,此时,参数的类型就是传入数据的类型。

  • 泛型方法的格式:

    [访问权限] <泛型> 返回类型 方法名([泛型标识 参数名称]) 抛出的异常

  • 泛型方法声明泛型时也可以指定上限

    1
    2
    3
    4
    5
    6
    7
    public class DAO {
    public <E> E get(int id, E e) {
    E result = null;
    return result;
    }
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
    for (T o : a) {
    c.add(o);
    }
    }
    public static void main(String[] args) {
    Object[] ao = new Object[100];
    Collection<Object> co = new ArrayList<Object>();
    fromArrayToCollection(ao, co);
    String[] sa = new String[20];
    Collection<String> cs = new ArrayList<>();
    fromArrayToCollection(sa, cs);
    Collection<Double> cd = new ArrayList<>();
    // 下面代码中T是Double类,但sa是String类型,编译错误。
    // fromArrayToCollection(sa, cd);
    // 下面代码中T是Object类型,sa是String类型,可以赋值成功。
    fromArrayToCollection(sa, co);
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class Creature{}
    class Person extends Creature{}
    class Man extends Person{}
    class PersonTest {
    public static <T extends Person> void test(T t){
    System.out.println(t);
    }
    public static void main(String[] args) {
    test(new Person());
    test(new Man());
    //The method test(T) in the type PersonTest is not
    //applicable for the arguments (Creature)
    test(new Creature());
    }
    }

3. 泛型的使用

3.1 泛型在集合中的使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package 集合中使用泛型;

import org.junit.Test;

import java.util.*;

public class GenericTest {
//在集合中使用泛型:以ArrayList为例
@Test
public void test1(){
ArrayList<Integer> arrayList = new ArrayList<>();//自动类型推断
arrayList.add(123);
arrayList.add(456);
arrayList.add(789);
//遍历方式一
for (Integer i:
arrayList) {
System.out.println(i);
}
//遍历方式二
Iterator<Integer> iterator = arrayList.iterator();
while (iterator.hasNext()){
int num = iterator.next();
System.out.println(num);
}

}

//在集合中使用泛型:以Map为例
@Test
public void test2(){
Map<String,Integer> map = new HashMap<>();
map.put("xiong",23);
map.put("zhuo",21);
map.put("guet",25);

Set<Map.Entry<String,Integer>> set = map.entrySet();
Iterator<Map.Entry<String,Integer>> iterator = set.iterator();
while (iterator.hasNext()){
Map.Entry<String,Integer> entry = iterator.next();
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println(key + "--->" + value);
/*
zhuo--->21
xiong--->23
guet--->25
*/
}
}
}

3.2 泛型类

泛型类型用于类的定义中,被称为泛型类。通过泛型可以完成对一组类的操作对外开放相同的接口。最典型的就是各种容器类,如:List、Set、Map。泛型类定义的泛型,在整个类中有效。如果被方法使用,那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了。为了让不同的方法可以操作不同类型,而且类型还不确定。那么可以将泛型定义在方法上。

泛型类

1
2
3
4
5
6
7
8
9
10
11
class Demo<T>
{
public void show(T t)
{
System.out.println("show: "+t);
}
public void print(T t)
{
System.out.println("show: "+t);
}
}
1
2
3
4
5
6
7
8
9
10
class GenericDemo4
{
public static void main(String[] args)
{
Demo<Integer>d = new Demo<Integer>();
d.show(new Integer(4));
Demo<String>d1 = new Demo<String>();
d1.print("haha");
}
}

show: 4
show: haha

3.3 泛型方法

泛型类,是在实例化类的时候指明泛型的具体类型;泛型方法,是在调用方法的时候指明泛型的具体类型 。

1
2
3
4
5
6
7
8
9
10
11
class Demo
{
public <T> void show(T t)
{
System.out.println("show: "+t);
}
public <Q> void print(Q q)
{
System.out.println("print:"+q);
}
}
1
2
3
4
5
6
7
8
9
class GenericDemo4
{
public static void main(String[] args)
{
Demo d = new Demo();
d.show("hello boy!");
d.print("Alex i love you !");
}
}

show: hello boy!
print:Alex i love you !

同时定义泛型类和泛型方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Demo<T>
{
public void show(T t)
{
System.out.println("show: "+t);
}
public <Q> void print(Q q)
{
System.out.println("print:"+q);
}
}
class GenericDemo4
{
public static void main(String[] args)
{
Demo <String> d = new Demo<String>();
d.show("hello boy!");
d.print("Alex i love you !");
d.print(5);
d.print("heiei");

}
}

show: hello boy!
print:Alex i love you !
print:5
print:heiei

特殊之处:
静态方法不可以访问类上定义的泛型
如果静态方法操作的应用数据类型不确定,可以将泛型定义在方法上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Demo<T>
{
public void show(T t)
{
System.out.println("show: "+t);
}
public <Q> void print(Q q)
{
System.out.println("print:"+q);
}

public static <W>void method(W t)
{
System.out.println("method: "+t);
}
}


class GenericDemo4
{
public static void main(String[] args)
{
Demo <String> d = new Demo<String>();
d.show("hello boy!");
d.print("Alex i love you !");

d.print(5);
d.print("heiei");

Demo.method("hihi");

}
}

show: hello boy!
print:Alex i love you !
print:5
print:heiei
method: hihi

3.4 泛型定义在接口上
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
interface Inter<T>
{
void show(T t);
}

//第一种
class InterImpl implements Inter<String>
{
public void show(String t)
{
System.out.println("show :"+t);
}
}

/*第二种
class InterImpl<T>implements Inter<T>
{
public void show(T t)
{
System.out.println("show :"+t);
}
}
*/
class GenericDemo5
{
public static void main(String[] args)
{
/*
InterImpl<Integer> i = new InterImpl<Integer>();
i.show(4);
*/
InterImpl i = new InterImpl();
i.show("haha");

}
}

show :haha

静态泛型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.util.ArrayList;
import java.util.List;

public class Test {

public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("test");

//普通转换
ArrayList<String> result1 = (ArrayList<String>) list;

//静态泛型转换
String result2 = convert(list);
}

private static <T> T convert(Object a) {
return (T) a;
}
}

上述代码是 编译通过,运行异常,为什么会出现这种现象呢?这是因为Java的泛型方法属于伪泛型,在编译的时候将进行类型擦除。普通的泛型方法在类构建时已经明确制定了对应的类型,而在静态泛型方法中,类型是无法直接推测的,缺少了明确的类型,最终造成类型转化异常。

convert函数最终转化后对应的字节码为 Method convert:(Ljava/lang/Object;)Ljava/lang/Object; 参数为Object类型,返回也为Object类型,而在接下来的 checkcast 操作中,由于 List 和 String 类型的不同,所以抛出了异常。

静态泛型相关问题

1
2
3
4
5
6
7
8
9
10
11
12
13
class Father<E>{
E name;
//编译通过
public static <T> void show1(T[] arr){
System.out.println(arr);
}

//编译不通过
public static void show2(E[]arr){
System.out.println(arr);
}

}

静态关键字修饰的是静态方法,然后我们给他加了泛型,至于为什么静态关键字可以修饰泛型方法,这就是Java的规定。

为什么第一个能编译通过,第二个不通过?

首先是静态方法都是通过 类名 来调用。前面的泛型T是声明了一个泛型,在这里可以通过extends等对泛型具体类型进行一个限定。T的具体类型由使用show1传入具体参数类型为准。

1
2
3
4
//编译通过
public static <T> void show1(T[] arr){
System.out.println(arr);
}

类泛型是在 创建对象 的时候才指定,静态方法是 通过类名 直接调用,当使用类名来调用show2方法时,引发Father类的初始化,静态变量/方法的声明是在类的初始化之前的,所以静态属性不能使用类泛型

第二个方法改成非静态方法就编译通过了,因为成员方法需要通过new对象来调用.

1
2
3
4
5
6
7
8
// 通过
public void show2(E[]arr){
System.out.println(arr);
}

Father<String> info = new Father<>();
String[] str = {};
info.show2(str);
3.5 泛型数组

查看sun的说明文档,在java中是”不能创建一个确切的泛型类型的数组”的。也就是说下面的这个例子是不可以的:

1
List<String>[] ls = new ArrayList<String>[10];  

而使用通配符创建泛型数组是可以的,如下面这个例子:

1
List<?>[] ls = new ArrayList<?>[10];

这样也是可以的:

1
List<String>[] ls = new ArrayList[10];

下面使用Sun的一篇文档的一个例子来说明这个问题:

1
2
3
4
5
6
7
List<String>[] lsa = new List<String>[10]; // Not really allowed.    
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
oa[1] = li; // Unsound, but passes run time store check
String s = lsa[1].get(0); // Run-time error: ClassCastException.
1
这种情况下,由于JVM泛型的擦除机制,在运行时JVM是不知道泛型信息的,所以可以给oa[1]赋上一个ArrayList而不会出现异常,但是在取出数据的时候却要做一次类型转换,所以就会出现ClassCastException,如果可以进行泛型数组的声明,上面说的这种情况在编译期将不会出现任何的警告和错误,只有在运行时才会出错。而对泛型数组的声明进行限制,对于这样的情况,可以在编译期提示代码有类型安全问题,比没有任何提示要强很多。

下面采用通配符的方式是被允许的:数组的类型不可以是类型变量,除非是采用通配符的方式,因为对于通配符的方式,最后取出数据是要做显式的类型转换的。

1
2
3
4
5
6
7
List<?>[] lsa = new List<?>[10]; // OK, array of unbounded wildcard type.    
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
oa[1] = li; // Correct.
Integer i = (Integer) lsa[1].get(0); // OK

4. 泛型在继承上的体现

如果B是A的一个子类型(子类或者子接口),而G是具有泛型声明的 类或接口,G并不是G的子类型! 比如:String是Object的子类,但是List并不是List 的子类。

image-20210729213718040

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package 泛型在继承中的体现;

import org.junit.Test;

import java.util.AbstractList;
import java.util.ArrayList;
import java.util.List;

public class GenericTest {
/*
泛型在继承方面的体现
虽然类A是类B的父类,但是G<A>和G<B>二者不具备父子关系,二者是并列关系
补充:类A是类B的父类,A<G>是B<G>的父类
*/
@Test
public void test1(){
Object obj = null;
String str = null;
obj = str;

Object[] arr1 = null;
String[] arr2 = null;
arr1 = arr2;

List<Object> list1 = null;
List<String> list2 = new ArrayList<>();
//此时的list1与list2不具有子父类关系,编译不通过
//list1 = list2;

AbstractList<String> list3 = null;
List<String> list4 = null;
ArrayList<String> list5 = null;

list3 = list5;
list4 = list5;
}
}

5. 通配符的使用

1.使用类型通配符:? 比如:List ,Map

List<?>是List、List等各种泛型List的父类。

2.读取List<?>的对象list中的元素时,永远是安全的,因为不管list的真实类型 是什么,它包含的都是Object。

3.写入list中的元素时,不行。因为我们不知道c的元素类型,我们不能向其中添加对象。

唯一的例外是null,它是所有类型的成员。

  • 将任意元素加入到其中不是类型安全的:

    Collection<?> c = new ArrayList();

    c.add(new Object()); // 编译时错误

    因为我们不知道c的元素类型,我们不能向其中添加对象。add方法有类型参数E作为集 合的元素类型。我们传给add的任何参数都必须是一个未知类型的子类。因为我们不知 道那是什么类型,所以我们无法传任何东西进去。

  • 唯一的例外的是null,它是所有类型的成员。

  • 另一方面,我们可以调用get()方法并使用其返回值。返回值是一个未知的类型,但是我们知道,它总是一个Object。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
package 通配符的使用;

import org.junit.Test;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class GenericTest {
/*
通配符的使用
通配符:?
类A是类B的父类,G<A>和G<B>是没有关系的,二者的共同父类是:G<?>
*/
@Test
public void test1(){
List<Object> list1 = null;
List<String> list2 = null;

List<?> list = null;

list = list1;
list = list2;


List<String> list3 = new ArrayList<>();
list3.add("AA");
list3.add("BB");
list3.add("CC");
list = list3;
//添加(写入):对于List<?>就不能向其内部添加数据
//除了添加null外
//list.add("DD");
list.add(null);
//获取(读取):允许读取数据,读取的数据类型为Object
Object o = list.get(0);
System.out.println(o);

print(list);//AA BB CC null
}

public void print(List<?> list){
Iterator<?> iterator = list.iterator();
while (iterator.hasNext()){
Object obj = iterator.next();
System.out.println(obj);
}
}

/*
有限制条件的通配符的使用:
? extends A: (-∞,A]
G<? extends A> 可以作为G<A>和G<B>的父类。其中B是A的子类
? super A:[A,+∞)
G<? super A> 可以作为G<A>和G<B>的父类。其中B是A的父类
*/
@Test
public void test2(){
List<? extends Person> list1 = null;
List<? super Person> list2 = null;

List<Studen> list3 = new ArrayList<>();
List<Person> list4 = new ArrayList<>();
List<Object> list5 = new ArrayList<>();

list1 = list3;
list1 = list4;
//list1 = list5; //Object并非Person的子类

//list2 =list3; //Student并非Person的父类
list2 = list4;
list2 = list5;

//读取数据
list1 = list3;
Person p1 = list1.get(0);
//编译不通过
//Student s1 = list1.get(0);
Studen s1 = (Studen) list1.get(0);

list1 = list4;
Person p2 = list1.get(0);
Studen s2 = (Studen) list1.get(0);//类型强转后可以通过编译,但是运行时会出错,

list2 = list4;
Object obj = list2.get(0);
//编译不通过
//Person p3 = list2.get(0);
Person p3 = (Person) list2.get(0);//类型强转之后可以通过编译

list2 = list5;
Object obj1 = list2.get(0);
Person p4 = (Person) list2.get(0);//类型强转后可以通过编译,但是运行时会出错,
}
}

class Person{}
class Studen extends Person{}

注意点

1
2
3
//注意点1:编译错误:不能用在泛型方法声明上,返回值类型前面<>不能使用?
public static <?> void test(ArrayList<?> list){
}
1
2
3
4
//注意点2:编译错误:不能用在泛型类的声明上
class GenericTypeClass<?>{
}

1
2
//注意点3:编译错误:不能用在创建对象上,右边属于创建集合对象
ArrayList<?> list2 = new ArrayList<?>();

有限的通配符

  • 允许所有泛型的引用调用
  • 通配符指定上限

    上限extends:使用时指定的类型必须是继承某个类,或者实现某个接口,即<=

  • 通配符指定下限

    下限super:使用时指定的类型不能小于操作的类,即>=

  • 举例:

    • (无穷小 , Number] 只允许泛型为Number及Number子类的引用调用
    • [Number , 无穷大) 只允许泛型为Number及Number父类的引用调用
    • 只允许泛型为实现Comparable接口的实现类的引用调用

6. 泛型应用实例

6.1 泛型嵌套
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void main(String[] args) {
HashMap<String, ArrayList<Citizen>> map = new HashMap<String, ArrayList<Citizen>>();
ArrayList<Citizen> list = new ArrayList<Citizen>();
list.add(new Citizen("刘恺威"));
list.add(new Citizen("杨幂"));
list.add(new Citizen("小糯米"));
map.put("刘恺威", list);
Set<Entry<String, ArrayList<Citizen>>> entrySet = map.entrySet();
Iterator<Entry<String, ArrayList<Citizen>>> iterator = entrySet.iterator();
while (iterator.hasNext()) {
Entry<String, ArrayList<Citizen>> entry = iterator.next();
String key = entry.getKey();
ArrayList<Citizen> value = entry.getValue();
System.out.println("户主:" + key);
System.out.println("家庭成员:" + value);
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package 自定义泛型结构;

public class GenericFruit{
public static void main(String[] args) {
Fruit fruit = new Fruit();
Apple apple = new Apple();
Person person = new Person();
Generic<Fruit> generic = new Generic<>();

generic.show1(fruit);//Fruit{}
generic.show1(apple);//Apple{}
//generic.show1(person);//

generic.show2(fruit);//Fruit{}
generic.show2(apple);//Apple{}
generic.show2(person);//Person{}

generic.show3(fruit);//Fruit{}
generic.show3(apple);//Apple{}
generic.show3(person);//Person{}
}
}

class Fruit{
@Override
public String toString() {
return "Fruit{}";
}
}
class Apple extends Fruit{
@Override
public String toString() {
return "Apple{}";
}
}
class Person{
@Override
public String toString() {
return "Person{}";
}
}
class Generic<T>{

public void show1(T t){
System.out.println(t.toString());
}

//在泛型类中声明了一个泛型方法,使用泛型E,这种泛型E可以为任意类型。可以类型与T相同,也可以不同。
//由于泛型方法在声明的时候会声明泛型<E>,因此即使在泛型类中并未声明泛型,编译器也能够正确识别泛型方法中识别的泛型。
public <E> void show2(E t){
System.out.println(t.toString());
}

//在泛型类中声明了一个泛型方法,使用泛型T,注意这个T是一种全新的类型,可以与泛型类中声明的T不是同一种类型。
public <T> void show3(T t){
System.out.println(t.toString());
}
}