本人小白一枚,欢迎大家一起讨论学习,如有错误,还望大家指教。
Java泛型
简述:JDK1.5版本以后出现的新特征,用于解决一个安全问题,是一个安全机制。
格式:通过<T>来定义要操作的引用数据类型,此处T的标识符随便写,常见的如T、E、K、V等形式的参数常用于表示泛型。
好处:
- 将运行时期出现的问题ClassException,转移到了编译时期。方便解决问题,让程序运行时问题减少,提高安全性。
- 避免了强制转换麻烦。
理解泛型只有在编译阶段有效
public static void main(String[] args) {
List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();
Class stringListClass = stringList.getClass();
System.out.println(stringListClass);
Class integerListClass = integerList.getClass();
System.out.println(integerListClass);
System.out.println(stringListClass.equals(integerListClass));
}
class java.util.ArrayList
class java.util.ArrayList
true
我们简单了解下Java泛型中类型擦除机制:Java的泛型是伪泛型,因为在编译期间,所有的泛型信息都会被擦除掉。正确理解泛型概念的首要前提是理解类型擦出。Java中的泛型基本都是在编译器这个层次来实现的,在生成的Java字节码中不包含泛型中的泛型信息的,会在编译器在编译的时候去掉。这个过程就称为类型擦除。如在代码中定义的List和List等类型,在编译后都会变成List。JVM看到的只是List,而由泛型附加的类型信息对JVM来说是不可见的。Java编译器会在编译时尽可能的发现可能出错的地方,但是仍然无法避免在运行时刻出现类型转换异常的情况。类型擦除也是Java的泛型实现方法与C++模版机制实现方式之间的重要区别。
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
List<Integer> list = new ArrayList<>();
list.add(123);
list.getClass().getMethod("add", Object.class).invoke(list, "张翼德");
System.out.println(list);
}
我们定义个集合并将泛型定义为Integer类型,如果直接调用add方法,我们只能存储整型数据。不过当我们用反射调用add方法的时候,我们就可以存储任意类型(Object)。这说明了泛型在编译之后被擦除了,只保留了原始类型(Object)。
在使用java提供对象时,什么时候写泛型呢?
通常在集合框架中很常见,只要见到<>就要定义泛型。
实例:按照集合中字符串的长度排序。
public class Demo1 {
public static void main(String[] args) {
TreeSet<String> arrayList = new TreeSet<String>(new MyCompare());
arrayList.add("ab");
arrayList.add("b");
arrayList.add("abc");
arrayList.add("edf");
arrayList.add("swxf");
for(Iterator iterator = arrayList.iterator();iterator.hasNext();) {
System.out.println(iterator.next());
}
}
}
class MyCompare implements Comparator {
@Override
public int compare(Object o1, Object o2) {
String str1 = (String)o1;
String str2 = (String)o2;
int num = str1.length() - str2.length();
if (num == 0) {
return str1.compareTo(str2);
}
return num;
}
}
泛型的使用
泛型有三种使用方式,分别是泛型类、泛型接口、泛型方法。
泛型类
什么时候定义泛型类?
当类中要操作的引用类型不确定的时候,早时期定义Object来完成扩展。现在定义泛型类来完成扩展。
格式:class 类名<泛型标识符>
定义的泛型Generic类,在我们创建该类时,不一样强制声明泛型类实参。如果声明了,此时就会起到限制作用,增强了安全性。如果不传入泛型类实参的话,我们在使用泛型的方法或成员变量定义的类型就可以是任意类型。
注意: 不能对确切的泛型类使用instanceof操作,如下面的写法编译会报错。
if (object instanceof Generid<String>)
泛型接口
泛型接口与泛型类定义及使用基本相同。泛型接口常被用于各种类的生产器中。
//定义一个泛型接口
public interface Generator<T> {
public T next();
}
当实现泛型接口
/**
* 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中
* 即:class FruitGenerator<T> implements Generator<T>{
* 如果不声明泛型,如:class FruitGenerator implements Generator<T>,编译器会报错:"Unknown class"
*/
class FruitGenerator<T> implements Generator<T>{
@Override
public T next() {
return null;
}
}
当实现泛型接口的类,传入泛型实参时:
/**
* 传入泛型实参时:
* 定义一个生产器实现这个接口,虽然我们只创建了一个泛型接口Generator<T>
* 但是我们可以为T传入无数个实参,形成无数种类型的Generator接口。
* 在实现类实现泛型接口时,如已将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型
* 即:Generator<T>,public T next();中的的T都要替换成传入的String类型。
*/
public class FruitGenerator implements Generator<String> {
private String[] fruits = new String[]{"Apple", "Banana", "Pear"};
@Override
public String next() {
Random rand = new Random();
return fruits[rand.nextInt(3)];
}
}
泛型方法
泛型类定义的类型,在整个类中有效,如果被方法使用,那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了。为了让不同的方法可以操作不同的类型,而且类型还不确定。那么可以将泛型定义在方法上。
注意:静态方法不可以访问类上定义的泛型。如果静态方法操作的应用数据类型不确定,可以将泛型定义在方法上。
class Demo
{
public <T> void show(T t)
{System.out.println("show:" + t);}
public <Q> void print(Q q) {
System.out.println("print:" + q);
}
//静态方法类型的写法
public static <W> void method(W t) {
System.out.println("metnod" + t);
}
}
泛型的高级应用
? 通配符,也可以理解为占位符。
泛型的限定:
? extends E:可以接收E类型或者E的子类型(上限)。
? super E:可以接收E类型或者E的父类型(下限)。
使用上限和?是有区别的,当使用?时,代表的是任意类型,而使用上下限代表着一个限定的范围。
public class Demo {
public static void main(String[] args) {
ArrayList<Person> personList = new ArrayList<>();
personList.add(new Person("刘德华"));
personList.add(new Person("刘丛印"));
printColl(personList);
ArrayList<Student> studentList = new ArrayList<>();
studentList.add(new Student("周润发"));
studentList.add(new Student("周星驰"));
printColl(studentList);
}
public static void printColl(Collection<? extends Person> collection) {
Iterator<? extends Person> iterator = collection.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next().getName());
}
}
}
class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
class Student extends Person{
Student(String name) {
super(name);
}
}
我们自定义Car类,之后传入到printColl()方法中,此时编译会失败。