泛型 详细讲解

目录

1. 基本介绍

1.1 传统方法的缺点

1.2 泛型的优点

1.3 泛型基本介绍

1.4 泛型的语法

(1)泛型的声明

(2)泛型的实例化

2. 注意事项和细节

3. 自定义泛型

3.1 自定义泛型类

(1)基本语法

(2)使用细节

3.2 自定义泛型接口

(1)基本语法

(2)使用细节

3.3 自定义泛型方法

(1)基本语法

(2)注意细节

4. 泛型的继承和通配符


1. 基本介绍

1.1 传统方法的缺点

(1)在不使用泛型,使用前面学过的知识,将元素添加到集合中时,不能对加入到集合中的数据类型进行约束,是不安全的。

(2)按照前面学过的知识,添加的元素是创建的对象时,对该集合进行遍历,需要进行类型转换(向下转型),如果集合数据量较大,会影响效率。

1.2 泛型的优点

(1)编译时,程序会检查添加元素的类型,提高了安全性。

(2)添加的元素是创建的对象时,可以直接取出对应的数据类型进行遍历,不需要进行类型转换,提高了效率。

(3)不再提示编译警告。

1.3 泛型基本介绍

(1)泛型可以理解为,表示数据类型的一种数据类型。可以表示多种数据类型(如:Integer、String、Dog等),具体表示的数据类型由程序员来决定。

(2)泛型又称参数化类型,是JDK 5.0出现的新特性,解决数据类型的安全性问题。

(3)在类声明或实例化时,只要指定需要的具体类型即可。

例:ArrayList类的底层定义是public class ArrayList<E> { },E被称为泛型。而我们在创建ArrayList类对象时,传入的Dog类型,是将Dog赋给E。

①ArrayList类底层定义源码:

②创建ArrayList类对象,传入的Dog类型:

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

(5)泛型的作用:可以在类声明时,通过一个标识 表示类中某个属性的类型,或者是某个方法的返回值的类型,或者是参数类型。

例:

①普通定义一个类:

s是String类型的数据,下面的构造器和方法都是String类型,以后在使用中也无法更改。

 

②使用泛型定义一个类:

定义一个类时,使用E表示泛型(不一定使用E,也可以使用其它字母,但一般使用E或T表示),然后使用E表示s的数据类型,下面的构造器和方法都是E这个数据类型。这个数据类型是在定义Person2的对象时,进行指定的。即 在编译期间,确定E是什么数据类型

创建对象时指定数据类型:

1.4 泛型的语法

(1)泛型的声明

class类名<E> { } 或 class类名<K, V> { }

interface 接口名<T> { }

说明:

①其中E,T,K,V不代表值,而是表示类型,由程序员自己决定。

②任意大写字母都可以。常用T表示,是Type的缩写。

(2)泛型的实例化

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

例:

2. 注意事项和细节

(1)在泛型实例化,进行类型参数指定时,只能指定引用类型(如:String、Integer、Double),不能是基本数据类型(如:int、double)。

(2)定义泛型使用的标识符一般都是大写字母,如:T、E。

(3)在指定泛型具体类型后,传参可以传入该类型 或 其子类类型。

例:

(4)泛型的使用形式

编译器会进行类型推断,补全< >中的内容,推荐使用简写。

(5)如果在泛型实例化时,没有给泛型指定具体的数据类型,则会默认指定E为Object类型。

3. 自定义泛型

3.1 自定义泛型类

(1)基本语法

class 类名<T, R...> { //…表示可以有多个泛型

//成员

}

例:

(2)使用细节

①普通成员(属性、方法)可以使用泛型。

②使用泛型的数组,不能初始化。因为数组在new的时候,不能确定泛型的类型,无法在内存开空间。

③静态方法和静态属性中不能使用类的泛型。因为静态方法和静态属性是和类相关的,在类加载时,对象还没有创建,就不能确定泛型的类型,JVM就无法完成初始化。

④泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定类型)。

⑤如果在创建对象时,没有指定泛型的类型,默认为Object。

3.2 自定义泛型接口

(1)基本语法

interface 接口名<T, R..> {

//成员

}

例:

(2)使用细节

①接口中,静态成员也不能使用泛型(和自定义泛型类规定一样)。

②泛型接口的类型,在 接口继承接口 或 类实现接口 时确定。

例1:接口继承接口时,指定泛型类型

例2:类实现接口时,直接指定泛型类型

③继承或实现接口时,没有指定泛型的类型,默认为Object。

3.3 自定义泛型方法

(1)基本语法

修饰符 <T, R…> 返回类型 方法名(参数列表) {

}

(2)注意细节

①泛型方法,可以定义在普通类中,也可以定义在泛型类中。

②当泛型方法被调用时,编译器会确定泛型的数据类型,不由程序员直接指定类型。

③在泛型类中,修饰符后 没有泛型标识符 的方法 不是泛型方法,而是使用了声明的泛型。

④泛型方法的参数,可以使用泛型类声明的泛型,也可以使用自己声明的泛型。

4. 泛型的继承和通配符

(1)泛型不具备继承性

(2)<?>  支持任意数据类型

(3)<? extends A>  支持A类以及A类的子类,规定了泛型的上限

(4)<? super A>  支持A类以及A类的父类,不限于直接父类,规定了泛型的下限

  • 39
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中的是一种强类机制,它可以让我们在编译时就可以检测到类错误,从而避免在运行时发生类错误。主要通过类参数来实现,可以在定义类、接口、方法时使用类参数,这样这些类、接口、方法就可以处理不同类的数据。 举例来说,假设我们需要定义一个可以存储任意类数据的容器。我们可以使用来实现: ```java public class Container<T> { private T data; public void setData(T data) { this.data = data; } public T getData() { return data; } } ``` 在上面的代码中,我们使用了一个类参数T来定义容器中存储的数据类。通过这个类参数,我们就可以在定义容器时指定数据类,例如: ```java Container<String> container1 = new Container<>(); container1.setData("Hello"); System.out.println(container1.getData()); // 输出:Hello Container<Integer> container2 = new Container<>(); container2.setData(123); System.out.println(container2.getData()); // 输出:123 ``` 在上面的代码中,我们分别定义了两个容器,一个存储字符串类的数据,另一个存储整数类的数据。由于使用了,我们可以在编译时就检测出类错误,从而避免在运行时发生类错误。 除了在类中使用,我们还可以在方法中使用。例如,假设我们需要定义一个方法,可以将一个数组中的元素逆序排列: ```java public static <T> void reverse(T[] array) { int i = 0; int j = array.length - 1; while (i < j) { T temp = array[i]; array[i] = array[j]; array[j] = temp; i++; j--; } } ``` 在上面的代码中,我们使用了一个类参数T来定义数组中元素的类。通过这个类参数,我们就可以在调用方法时指定数组中元素的类,例如: ```java Integer[] array1 = {1, 2, 3, 4, 5}; reverse(array1); System.out.println(Arrays.toString(array1)); // 输出:[5, 4, 3, 2, 1] String[] array2 = {"a", "b", "c", "d", "e"}; reverse(array2); System.out.println(Arrays.toString(array2)); // 输出:[e, d, c, b, a] ``` 在上面的代码中,我们分别定义了两个数组,一个存储整数类的数据,另一个存储字符串类的数据。由于使用了,我们可以在编译时就检测出类错误,从而避免在运行时发生类错误。同时,我们可以在调用方法时指定数组中元素的类,从而避免了类转换的麻烦。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值