目录
相关文章:
很全面的一篇文章:Java 泛型,你了解类型擦除吗?
深入偏难的一篇文章:泛型的理解及应用(一):泛型擦除
一、概述
JDK1.5以后出现的安全机制
好处:
1、将运行时期的问题ClassCastException转到了编译时期;
2、避免了强制转换的麻烦;
什么时候用?当操作的引用数据类型不确定的时候。就使用<>。将要操作的引用数据类型传入即可,其实<>就是一个用于接收具体引用数据类型的参数范围。
在程序中,只要用到了带有<>的类或者接口,就要明确传入的具体引用数据类型。
泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。
二、举例
public class GenericDemo1 {
public static void main(String[] args) {
ArrayList list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add(4);
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
String string = (String) iterator.next();
System.out.println(string);
}
}
}
毫无疑问,程序的运行结果会以崩溃结束:
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
ArrayList可以存放任意类型,例子中添加了一个String类型,添加了一个Integer类型,再使用时都以String的方式使用,因此程序崩溃了。为了解决类似这样的问题(在编译阶段就可以解决),泛型应运而生。
三、特性
Java分为编译时期和运行时期。而泛型是用于编译时期的技术(即给编译器使用),确保了类型的安全,从而提高了编译时期的安全性。
在运行时期,会将泛型去掉,生成的class文件中是不带泛型的,这个称之为泛型的擦除。
为什么要擦除呢?为了兼容运行时的类加载器。因为Java5及之前的类加载器是没有泛型的,识别不了泛型。
但是,一开始的问题又来了!!擦除后,如果你要获取数据的时候又要考虑类型转换的问题(数据的类型被擦除后,都变成了Object类型),那么,这个问题怎么解决呢?如果自己手动强转,那么之前做的就没有意义了。
解决方案就是泛型的补偿机制,这个补偿机制是在类加载器的基础上拓展的,这就意味着之前的类加载器的一套东西不需要改动,在运行时,通过getClass() 获取元素的类型进行自动的类型转换。不用使用者再进行强制转换了。
泛型擦除的体现:
public class GenericDemo2 {
public static void main(String[] args) {
List<String> stringArrayList = new ArrayList<String>();
List<Integer> integerArrayList = new ArrayList<Integer>();
Class classStringArrayList = stringArrayList.getClass();
Class classIntegerArrayList = integerArrayList.getClass();
System.out.println(classStringArrayList);
System.out.println(classIntegerArrayList);
if(classStringArrayList.equals(classIntegerArrayList)){
System.out.println("类型相同");
}
}
}
输出结果:
class java.util.ArrayList
class java.util.ArrayList
类型相同
通过上面的例子可以证明,在编译之后程序会采取去泛型化的措施。也就是说Java中的泛型,只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段。
对此总结成一句话:泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型。
四、泛型的使用
1、泛型类
public class GenericDemo4 {
public static void main(String[] args) {
Tool<Student> tool = new Tool<>();
tool.setType(new Student());
Student student = tool.getType();
}
}
public class Tool<T> {
private T t;
public T getType() {
return t;
}
public void setType(T t) {
this.t = t;
}
}
public class Student extends Person {
public Student() {
}
public Student(String name, int age) {
super(name, age);
}
}
public class Person implements Comparable<Person> {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Person person) {
int temp = this.age - person.age;
return temp == 0 ? this.name.compareTo(person.name) : temp;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
2、泛型方法
public class Tool<T> {
private T t;
public T getType() {
return t;
}
public void setType(T t) {
this.t = t;
}
/**
* 将泛型定义在方法上
* @param str
* @param <W>
*/
public <W> void show(W str) {
System.out.println("show: " + str);
}
public void print(T str) {
System.out.println("print: " + str);
}
/**
* 当方法静态时,不能访问类上定义的泛型。如果静态方法使用泛型,只能将泛型定义在方法上
* @param str
* @param <Y>
*/
public static <Y> void method(Y str){
System.out.println("method: " + str);
}
}
public class GenericDemo5 {
public static void main(String[] args) {
Tool<String> tool = new Tool<>();
tool.show(4);
tool.print("haha");
Tool.method("hehehe");
Tool.method(88);
}
}
3、泛型接口
public class GenericDemo6 {
public static void main(String[] args) {
InterImpl inter = new InterImpl();
inter.show("abc");
InterImpl2<String> inter2 = new InterImpl2<String>();
inter2.show("abc222");
}
}
interface Inter<T> {
void show(T t);
}
class InterImpl implements Inter<String> {
@Override
public void show(String s) {
System.out.println("show:" + s);
}
}
class InterImpl2<T> implements Inter<T> {
@Override
public void show(T t) {
System.out.println("show:" + t);
}
}
4、通配符的体现
public class GenericDemo7 {
public static void main(String[] args) {
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("abc");
arrayList.add("haha");
ArrayList<Integer> arrayList2 = new ArrayList<>();
arrayList2.add(111);
arrayList2.add(222);
printCollection(arrayList);
printCollection(arrayList2);
}
/**
* 泛型的通配符:?
* @param collection
*/
private static void printCollection(Collection<?> collection) {
Iterator<?> iterator = collection.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
5、泛型限定
? extends E:接受E类型或者其子类型对象 上限!
? super E: 接受E类型或者其父类型对象 下限!
一般存储元素的时候都是用上限,因为这样取出的都是按照上限类型来运算的,不会出现类型安全隐患。
public class GenericDemo8 {
public static void main(String[] args) {
ArrayList<Worker> arrayList = new ArrayList<>();
arrayList.add(new Worker("张三",27));
arrayList.add(new Worker("李四",30));
ArrayList<Student> arrayList2 = new ArrayList<>();
arrayList2.add(new Student("小明",17));
arrayList2.add(new Student("小芳",18));
printCollection(arrayList);
printCollection(arrayList2);
}
/**
* 泛型的通配符:?
* 泛型的高级应用:泛型限定
* @param collection
*/
private static void printCollection(Collection<? extends Person> collection) {
Iterator<?> iterator = collection.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
public class Student extends Person {
public Student() {
}
public Student(String name, int age) {
super(name, age);
}
@Override
public String toString() {
return "Student{" +
"name='" + getName() + '\'' +
", age=" + getAge() +
'}';
}
}
public class Worker extends Person {
public Worker() {
}
public Worker(String name, int age) {
super(name, age);
}
@Override
public String toString() {
return "Worker{" +
"name='" + getName() + '\'' +
", age=" + getAge() +
'}';
}
}
五、查阅集合的一些技巧:
需要唯一吗?
需要:set
需要指定顺序吗?
需要:TreeSet
不需要:HashSet
但是想要一个和存储一致的顺序(有序):LinkedHashSet
不需要:List
需要频繁增删吗?
需要:LinkedList
不需要:ArrayList
看到array:就要想到数组,查询快,有角标。
看到link:就要想到链表,就要想到增删快,就要想到add,get,remove+first last的方法
看到hash:就要想到哈希表,就要想到唯一性,就要想到元素需要覆盖hashcode方法和equals方法
看到tree:就要想到二叉树,就要想到排序,就要想到两个接口Comparable,Comparator