一:什么是泛型?

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

二:为什么要有泛型?

1:解决元素存储的安全性问题,例如商品、药品标签,不会弄错。

2:解决获取数据元素时,需要类型强制转换的问题,例如不用每次拿商品、药品都要辨别。

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

三:泛型的使用:

JDK 5.0新增的特性。

在集合中使用泛型:

  • 集合接口或集合类在jdk 5.0时都修改为带泛型的结构。
  • 在实例化集合类时,可以指明具体的泛型类型。
  • 指明完以后,在集合类或接口中凡事定义类或接口时,内部结构(比如:方法、构造器、属性等)使用到类的泛型的位置,都指定为实例化的泛型类型。例如:add(E e)—->实例化以后,add(Interger e)
  • 注意:泛型的类型必须是类,不能是基本数据类型,需要用到基本数据类型的位置,拿包装类替换
  • 如果实例化时,没有指明泛型的类型,默认类型为java.lang.Object类型。

Demo

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
public class GenericTest {


//在集合中使用泛型之前的情况:
@Test
public void test1(){
ArrayList list = new ArrayList();
//需求:存放学生的成绩
list.add(78);
list.add(76);
list.add(89);
list.add(88);
//问题一:类型不安全

// list.add("Tom");

for(Object score : list){
//问题二:强转时,可能出现ClassCastException
int stuScore = (Integer) score;

System.out.println(stuScore);

}

}

//在集合中使用泛型的情况:以ArrayList为例
@Test
public void test2(){
ArrayList<Integer> list = new ArrayList<Integer>();

list.add(78);
list.add(87);
list.add(99);
list.add(65);
//编译时,就会进行类型检查,保证数据的安全

// list.add("Tom");

//方式一:

// for(Integer score : list){
// //避免了强转操作
// int stuScore = score;
//
// System.out.println(stuScore);
//
// }
//方式二:
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()){
int stuScore = iterator.next();
System.out.println(stuScore);
}

}

//在集合中使用泛型的情况:以HashMap为例
@Test
public void test3(){

// Map<String,Integer> map = new HashMap<String,Integer>();
//jdk7新特性:类型推断
Map<String,Integer> map = new HashMap<>();

map.put("Tom",87);
map.put("Jerry",87);
map.put("Jack",67);

// map.put(123,"ABC");
//泛型的嵌套
Set<Map.Entry<String,Integer>> entry = map.entrySet();
Iterator<Map.Entry<String, Integer>> iterator = entry.iterator();

while(iterator.hasNext()){
Map.Entry<String, Integer> e = iterator.next();
String key = e.getKey();
Integer value = e.getValue();
System.out.println(key + "----" + value);
}

}


}

四:自定义泛型结构:

  1. 泛型的声明

    interface List 和 class Gen Test<K,V>

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

  2. 泛型的实例化

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

    List<String> strList = new ArrayList<String>();

    Iterator<Customer> iterator = customers.iterator();

    • T只能是类,不能用基本数据类型填充。但可以使用包装类填充。

    • 把一个集合中的内容限制为一个特定的数据类型,这就是generics(泛型)背后的核心思想。

    Demo

    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
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    public class GenericTest1 {

    @Test
    public void test1(){
    //如果定义了泛型类,实例化没有指明类的泛型,则认为此泛型类型为Object类型
    //要求:如果大家定义了类是带泛型的,建议在实例化时要指明类的泛型。
    Order order = new Order();
    order.setOrderT(123);
    order.setOrderT("ABC");

    //建议:实例化时指明类的泛型
    Order<String> order1 = new Order<String>("orderAA",1001,"order:AA");

    order1.setOrderT("AA:hello");

    }

    @Test
    public void test2(){
    SubOrder sub1 = new SubOrder();
    //由于子类在继承带泛型的父类时,指明了泛型类型。则实例化子类对象时,不再需要指明泛型。
    sub1.setOrderT(1122);

    SubOrder1<String> sub2 = new SubOrder1<>();
    sub2.setOrderT("order2...");
    }

    @Test
    public void test3(){

    ArrayList<String> list1 = null;
    ArrayList<Integer> list2 = new ArrayList<Integer>();
    //泛型不同的引用不能相互赋值。

    // list1 = list2;

    Person p1 = null;
    Person p2 = null;
    p1 = p2;


    }

    //测试泛型方法
    @Test
    public void test4(){
    Order<String> order = new Order<>();
    Integer[] arr = new Integer[]{1,2,3,4};
    //泛型方法在调用时,指明泛型参数的类型。
    List<Integer> list = order.copyFromArrayToList(arr);

    System.out.println(list);
    }

    }

    ----------------------------------------------------------------------------------------

    public class Order<T> {

    String orderName;
    int orderId;

    //类的内部结构就可以使用类的泛型

    T orderT;

    public Order(){
    //编译不通过
    // T[] arr = new T[10];
    //编译通过
    T[] arr = (T[]) new Object[10];
    }

    public Order(String orderName,int orderId,T orderT){
    this.orderName = orderName;
    this.orderId = orderId;
    this.orderT = orderT;
    }

    //如下的三个方法都不是泛型方法
    public T getOrderT(){
    return orderT;
    }

    public void setOrderT(T orderT){
    this.orderT = orderT;
    }

    @Override
    public String toString() {
    return "Order{" +
    "orderName='" + orderName + '\'' +
    ", orderId=" + orderId +
    ", orderT=" + orderT +
    '}';
    }
    //静态方法中不能使用类的泛型。
    // public static void show(T orderT){
    // System.out.println(orderT);
    // }

    public void show(){
    //编译不通过
    // try{
    //
    //
    // }catch(T t){
    //
    // }

    }

    //泛型方法:在方法中出现了泛型的结构,泛型参数与类的泛型参数没有任何关系。
    //换句话说,泛型方法所属的类是不是泛型类都没有关系。
    //泛型方法,可以声明为静态的。原因:泛型参数是在调用方法时确定的。并非在实例化类时确定。
    public static <E> List<E> copyFromArrayToList(E[] arr){

    ArrayList<E> list = new ArrayList<>();

    for(E e : arr){
    list.add(e);
    }
    return list;

    }
    }

    -----------------------------------------------------------------------------------------------

    public class SubOrder extends Order<Integer> {//SubOrder:不是泛型类


    public static <E> List<E> copyFromArrayToList(E[] arr){

    ArrayList<E> list = new ArrayList<>();

    for(E e : arr){
    list.add(e);
    }
    return list;

    }


    }

    -------------------------------------------------------------------------------------------------
    • 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:<E1,E2,E2>
    • 泛型类的构造器如下:public GenericClass(){}
      • 错误写法:public GenericClass<E>(){}
    • 实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致。
    • 泛型不同的引用不能相互赋值。

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

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

    • 如果范型结构是一个接口或抽象类,则不可创建泛型类的额对象。

    • JDK 1.7,泛型的简化操作:ArrayList<Fruit> flist = new ArrayList<>();

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

    • 在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用类的泛型。

    • 异常类不能是泛型的。

    • 不能使用new E[]。但是可以:E[] elements = (E[])new Object[capacity];

      • 参考:ArrayList源码中声明:Object[] elenmentData,而非泛型参数类型数组。
    • 子类有泛型,子类可以选择保留泛型也可以选择指定泛型类型:

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

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

    • 方法!也可以被泛型化,不管此时定义在其中的类是不是泛型类。在泛型方法中可以定义泛型参数,此时,参数的类型就是传入数据的类型。

    • 泛型方法的格式:

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

    • 泛型方法声明泛型时也可以指定上限
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class DAO {

    public &lt;E&gt; 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
    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
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    /**
    *

    1. 泛型在继承方面的体现


    2. 通配符的使用


    1. 泛型在继承方面的体现

    虽然类A是类B的父类,但是G<A> 和G<B>二者不具备子父类关系,二者是并列关系。

    补充:类A是类B的父类,A<G> 是 B<G> 的父类

    */


    public class GenericTest {

    @Test
    public void test1(){

    Object obj = null;
    String str = null;
    obj = str;

    Object[] arr1 = null;
    String[] arr2 = null;
    arr1 = arr2;
    //编译不通过

    // Date date = new Date();
    // str = date;
    List<Object> list1 = null;
    List<String> list2 = new ArrayList<String>();
    //此时的list1和list2的类型不具有子父类关系
    //编译不通过
    // list1 = list2;
    /*
    反证法:
    假设list1 = list2;
    list1.add(123);导致混入非String的数据。出错。

    */

    show(list1);
    show1(list2);

    }



    public void show1(List<String> list){

    }

    public void show(List<Object> list){

    }

    @Test
    public void test2(){

    AbstractList<String> list1 = null;
    List<String> list2 = null;
    ArrayList<String> list3 = null;

    list1 = list3;
    list2 = list3;

    List<String> list4 = new ArrayList<>();

    }

    /*
    2. 通配符的使用
    通配符:?

    类A是类B的父类,G<A>和G<B>是没有关系的,二者共同的父类是:G<?>


    */

    @Test
    public void test3(){
    List<Object> list1 = null;
    List<String> list2 = null;

    List<?> list = null;

    list = list1;
    list = list2;
    //编译通过

    // print(list1);
    // print(list2);


    //
    List<String> list3 = new ArrayList<>();
    list3.add("AA");
    list3.add("BB");
    list3.add("CC");
    list = list3;
    //添加(写入):对于List<?>就不能向其内部添加数据。
    //除了添加null之外。

    // list.add("DD");
    // list.add('?');

    list.add(null);

    //获取(读取):允许读取数据,读取的数据类型为Object。
    Object o = list.get(0);
    System.out.println(o);


    }

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

    /*
    3.有限制条件的通配符的使用。
    ? extends A:
    G<? extends A> 可以作为G<A>和G<B>的父类,其中B是A的子类

    ? super A:
    G<? super A> 可以作为G<A>和G<B>的父类,其中B是A的父类

    */
    @Test
    public void test4(){

    List<? extends Person> list1 = null;
    List<? super Person> list2 = null;

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

    list1 = list3;
    list1 = list4;

    // list1 = list5;

    // list2 = list3;
    list2 = list4;
    list2 = list5;

    //读取数据:
    list1 = list3;
    Person p = list1.get(0);
    //编译不通过
    //Student s = list1.get(0);

    list2 = list4;
    Object obj = list2.get(0);
    ////编译不通过

    // Person obj = list2.get(0);

    //写入数据:
    //编译不通过

    // list1.add(new Student());

    //编译通过
    list2.add(new Person());
    list2.add(new Student());

    }

    }

    通配符的使用:

  • 使用类型的通配符:?

    • 比如:List<?>,Map<?,?>
    • List<?>是List<String>、List<Object>等各种泛型List的父类。
  • 读取List<?>的对象list中的元素时,永远是安全的,因为不管list的真实类型是什么,它包含的都是Object。

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

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

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

      Collection <?> c = new ArrayList<String>();

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

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

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

通配符的使用:注意点

1
2
3
4
5
6
7
8
9
10
11
12
13
// 注意点1:编译错误:不能用在泛型方法的声明上,返回值类型前面<>不能使用?

public static <?> void test(ArrayList<?> list){

}

// 注意点2:编译错误:不能用在泛型类的声明上。
class GenericTypeClass<?>{

}

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

通配符的使用:有限制的通配符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?>	允许所有泛型的引用调用。
--------------------------------------------------------------------------------------------------------
通配符指定上限:
上限extends:使用时指定的类型必须是继承某个类,或者实现某个接口,即<=
--------------------------------------------------------------------------------------------------------
通配符指定下限:
下限super:使用时指定的类型不能小于操作的类,即>=
--------------------------------------------------------------------------------------------------------
举例:
<? extends Number> (无穷小,Number]
只允许泛型为Number及Number子类的引用调用。

<? super Number> [Number, 无穷大)
只允许泛型为Number及Number父类的引用调用。

<? extends Comparable>
只允许泛型为实现Comparaable接口的实现类的引用调用。