Java泛型-泛型作用+泛型在各种数据结构中的使用+自定义泛型
文章目录
1. 概念
-
所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用时〈例如,继承或实现这个接口,用这个类型声明变量、创建对象时) 确定〈即传入实际的类型参数,也称为类型实参) 。
-
从JDK1.5以后,Java引入了“参数化类型(Parameterized type) ”的概念,允许我们在创建集合时再指定集合元素的类型,正如: List, 这表明该List只能保存字符串类型的对象。
-
JDK1.5改写了集合框架中的全部接口和类,为这些接口、类增加了泛型支持,从而可以在声明集合变量、创建集合对象时传入类型实参。
2. 为什么要使用泛型(Generic)
拿集合来举例
-
集合在jdk 5 时,全部改为带泛型的结构
-
在实例化集合类时,可以指明具体的泛型类型
-
指明后,在集合或类或接口中,凡是定义类或接口,内部结构(可以是 方法,构造器,属性等)使用到类的泛型的位置,都指定为实例化的泛型类型。
例如:在下面的代码中,ArryList指定为Integer,add(E e) 就变成了 add(Iteger e)
-
泛型的类型必须是一个类,不能是基本数据类型。如果想用到基本类型,那就要用它们的包装类
-
如果在实例化时,没有指明泛型的类型,则默认使用java.lang.Object类型。
2.1 在ArrayList中使用泛型
-
没有泛型的话,集合里面存储数据的类型就是Object,所以说,在某些情况下会出现我们不希望的类型进入这个集合,所以有类型安全的问题。
-
因为集合里都是当作Object类型,所以在我从集合中取数据的时候,要通过强制转换,将Object类型,强制转换成我们存入之前对象的类型。一旦有其他类型的对象混入,这里就会出现 ClassCastException。
ArrayList list = new ArrayList();
list.add(90);
list.add(92);
list.add(95);
list.add(91);
// 类型不安全:
// 因为add的参数是Object类型,所以字符串也能被添加成功,所以就存在危险。
list.add("asdasd");
for (Object score : list){
// 强转时,可能会出现ClassCastException异常
int stuscore = (int) score;
System.out.println(stuscore);
}
当我们使用了泛型之后,在编译时就会进行类型检查,如果混入其他类型,在编译的时候就会抛出异常
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fj1rwlGw-1603244628739)(/Users/luca/MarkText-img-Support/2020-07-01-19-47-14-image.png)]
2.2 在HashMap中使用泛型
//java7 之前应该这样写,之后就可以省略了
//HashMap<String, Integer> map = new HashMap<String, Integer>();
HashMap<String, Integer> map = new HashMap<>();
map.put("luca",123);
map.put("lucy",122);
map.put("lu",125);
map.put("lucas",121);
Set<Map.Entry<String, Integer>> entry = map.entrySet();
Iterator<Map.Entry<String, Integer>> ite = entry.iterator();
while(ite.hasNext()){
Map.Entry<String, Integer> e = ite.next();
String name = e.getKey();
int score = e.getValue();
System.out.println(name + score);
}
3. 自定义泛型结构
3.1 泛型类与泛型接口
-
类上面加一个<> 就是泛型类,也就是说使用了泛型的类就叫做泛型类,泛型类与泛型接口没什么太大的却别,区别就是类与接口的区别,所以拿泛型类为例。
-
如果有一个类,这个类有一个属性,但是这个属性的类型不确定,这个属性的类型要在实例化的时候才能确定。这时我们就可以在这个类中使用泛型,我们之需要在声明类的时候加上一个<>,里面的字母一般为大写一般没 T,O,E,K,V 等
// 这样我们就可以在Order类中使用泛型了,这个T就称为类的泛型
public class Order<T> {
String ordername;
int orderid;
//使用T——类的泛型
T ordert;
// 有类的泛型的构造器
public Order(String ordername,int orderid,T ordert){
this.orderid = orderid;
this.ordername = ordername;
this.ordert = ordert;
}
// 类的泛型的get set 方法。 其实和平时没有多大区别
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 +
'}';
}
}
以上就是一个泛型类
3.2 泛型类的子类
当一个子类继承了一个泛型类时,在继承的时候就有两种情况:
- 子类在继承泛型类时就指明这个泛型
// 在继承Order时,就直接指明这个泛型,这时这个子类就时一个普通类了
public class subOrder extends Order<Integer> {
}
-
继承时不指明 类似于ArrayList
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable // 此时 subOrder仍是一个泛型类 public class subOrder extends Order<T> { }
3.3 自定义泛型类/接口 仍需注意的点
// 4. 泛型不同的引用不能相互赋值
// 下面list1和list2 虽然都是 ArrayList类型,但是由于它们的泛型不同,所以不能相互赋值
ArrayList<String> list1 = new ArrayList<>();
ArrayList<Integer> list2 = new ArrayList<>();
list1 = list2; // 这是错误的
3.5 泛型方法
// 取自Collection源码
public interface Collection<E> extends Iterable<E>{
<T> T[] toArray(T[] a); // 这个是泛型方法
boolean add(E e); // 这个不是
}
泛型方法只是针对于方法,与这个方法的类/接口是不是泛型的无关。
例子:假设想写一个复制数组的方法,将一个数组作为参数,在放到一个新的数组中,并将其返回。 这时我们不知道这个参数数组里面是什么类型的,而且不管是什么类型的的元素我们都应该能复制,所以这时这个方法就要使用泛型
// 这样写是不对的,因为编译器把这句话理解称:返回E类型的数组,输入E类型的数组;
// 它以为E就是一个类,所以为了告诉编译器这个是泛型,我们要在前面加一个<E>
public List<E> copyList(E[] inputlist){
//返回E类型的数组 // 输入E类型的数组
}
public <E> List<E> copyList(E[] inputlist){
}
// copyList的使用
Integer[] arry = {1,2,3,4};
List<Integer> list = order.copyList(arr);
泛型方法可以声明为静态的,因为泛型参数是在方法调用时确定的,并不是在实例化时确定的。
上一篇: Access入门教程3.1向导简介
下一篇: mysql数据库的主从同步