泛型
一、泛型的引出
代码示例
public class pra {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add("java");
arrayList.add("jack");
arrayList.add("jom");
arrayList.add(new a());
for (Object obj :arrayList) {
String str = (String)obj;
System.out.println(str);
}
}
}
class a{
public a() {
}
}
由于添加了 a 对象,但是在遍历的时候遍历的是字符串对象,这个时候就会抛出ClassCastException
异常
问题总结
-
不能对加入到集合 ArrayList 中的数据类型进行约束(不安全)
-
遍历的时候需要进行数据类型转换,如果集合的数据量大,对显效率有影响
总结:由于集合中添加的对象,当集合中的对象不为同一类对象时,遍历集合就会出现异常,这个时候可以通过泛型来约束集合存储的对象类型
二、泛型
1. 基本介绍
(1)泛型又称参数化类型,是 Jdk5.0 出现的新特性,解决数据类型的安全性问题
(2)在类声明或实例化时只要指定好需要的具体的类型即可。
(3)Java 泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生 ClassCastException 异常。同时,代码更加简洁、健壮
(4)泛型的作用是:可以在类声明时通过一个标识表示类中某个属性的类型,或者是某个方法的返回值的类型,或者是参数类型。
(5)泛型的理解:是一种表示数据类型的数据类型
2. 泛型的使用
(1)基本语法
单例集合
ArrayList<T> arrylist = new ArrayList<T>()
双例集合
HashMap<K,V> arrylist = new HashMap<K,V>()
推荐写法
ArrayList<String> arrylist = new ArrayList<>()
说明:T、K、V 不代表值(可以替换成其他任意字母),而是表示类型,
(2)应用场景
1. 接口
interface 接口名<T>{
}
2. 类
class 类 <K,T....>{
}
注意:类中可以有多个泛型变量
3. 泛型的优点
-
编译时,检查添加元素的类型,提高了安全性
-
减少了类型转换的次数,提高效率
-
不再提示编译警告
4. 泛型的使用细节
-
泛型变量只能是引用类型(错误的例子:List<int> list = new ArrayList<int>()
)
-
给泛型指定具体类型后,可以传入该类型或者其子类型(理解:该集合只能存储指定的类型和子类型)
-
如果不写泛型默认是Object
类型,下方的两条语句等价
ArrayList list = new ArrayList();
ArrayList<Object> list = new ArrayList<>()
传入子类型的说明
public class pra {
public static void main(String[] args) {
animal<a> a = new animal<a>(new a());
animal<a> b = new animal<a>(new b());
}
}
class a{
}
class b extends a{
}
class animal<T>{
T e;
public animal(T e){
this.e = e;
}
public void getclass(){
System.out.println(e.getClass());
}
}
说明:指定了animal
类只能存储 a 类对象,但是 b 类是 a 的子类,即 animal 类既可以是 a 对象也可以是 b 对象
5. 简单的泛型案例
public class pra {
public static void main(String[] args) {
HashMap<String,student> hashMap = new HashMap<>();
hashMap.put("18",new student("jackson"));
hashMap.put("20",new student("jack"));
hashMap.put("23",new student("jom"));
Set<Map.Entry<String, student>> entries = hashMap.entrySet();
Iterator<Map.Entry<String, student>> iterator = entries.iterator();
while (iterator.hasNext()) {
Map.Entry<String, student> next = iterator.next();
System.out.println("key:" + next.getKey() + " value:" + next.getValue());
}
}
}
class student{
String name;
public student(String name) {
this.name = name;
}
@Override
public String toString() {
return "student{" +
"name='" + name + '\'' +
'}';
}
}
通过指定 HashMap 存储键值对的类型,然后遍历集合查看存储结果
三、自定义泛型类
1. 基本介绍
(1) 普通成员可以使用泛型(属性、方法)
(2) 使用泛型的数组,不能初始化(由于不知道数组的类型,无法在内存开辟空间)
(3) 静态方法中不能使用类的泛型
-
因为静态是和类相关的,在类加载时,对象还没有创建
-
如果静态方法和静态属性使用了泛型,JVM 就无法完成初始化
(4) 泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定类型)
(5) 如果在创建对象时,没有指定类型,默认为 Object
(6)泛型是标识符,需要大写
结构示例
class a<T,K,V...>{
T age;
K name;
V phone_number;
...
}
四、自定义泛型接口
(1) 接口中,静态成员也不能使用泛型
(2) 泛型接口的类型,在继承接口或者实现接口时确定
(3) 没有指定类型,默认为 Object
(4)如果一个类实现了泛型接口,需要实现接口中的方法并且需要指定接口方法中的泛型类型
代码示例
public class pra {
public static void main(String[] args) {
}
}
interface a<T,R>{
void hi(T t,R r);
void pay(T t,R r);
void run(T t,R r);
}
class b implements a<String,Double>{
@Override
public void hi(String s, Double aDouble) {
}
@Override
public void pay(String s, Double aDouble) {
}
@Override
public void run(String s, Double aDouble) {
}
}
代码分析
通过指定泛型的类型来实现接口中的方法
五、自定义泛型方法
(1)实体信息为泛型方法的定义位置相关内容,泛型方法可定义在普通类或泛型类中
(2)泛型方法调用时确定类型(通过传入的参数自动判断是什么类型)
(3)若修饰符后无 <T,R...>
,像 public void eat(E e) {}
不是泛型方法,只是方法参数使用了泛型
六、泛型的继承和通配符
(1)泛型不具备继承性(错误案例:List<Object> list = new ArrayList<String>()
)
(2) <?>
: 支持任意泛型类型
(3) <? extends A>
: 支持 A 类以及 A 类的子类,规定了泛型的上限
(4) <? super A>
: 支持 A 类以及 A 类的父类,不限于直接父类,规定了泛型的下限