目录
● 为什么用泛型
早期的Object类型可以接收任意的对象类型,但是在实际的使用中,会有类型转换的问题。也就存在这隐患,所以Java提供了泛型来解决这个安全问题。
public class Demo1 {
public static void main(String[] args) {
/*
默认情况下集合类可以添加任意类型(Object)
*/
ArrayList arrayList = new ArrayList<>();
arrayList.add("abc");
arrayList.add(1);
arrayList.add("asd");
arrayList.add(true);
arrayList.add(2.2);
//后期处理容易出现类型转换问题
for (Object obj : arrayList ){
//具体操作,需要向下转型
if (obj instanceof String ) {
String s = (String)obj;
s.length();
}
if (obj instanceof Integer ) {
}
if (obj instanceof Double ) {
}
}
}
}
● 什么是泛型
● 泛型,即“参数化类型” 。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。
● 参数化类型,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式,然后在使用/调用时传入具体的类型。
● Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,泛型的好处就是在编译的时候能够检查类型安全。
● 泛型类
泛型类型用于类的定义中,被称为泛型类。通过泛型可以完成对一组类的操作对外开放相同的接口。
一个最普通的泛型类:
public class Demo<T>{ //T可以为任意标识符,常见的如T、E、K、V等形式的参数常用于表示泛型
private T key; //key这个成员变量的类型为T,T的类型由外部指定
public Demo(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定
this.key = key;
}
public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定 return key;
}
}
传入的实参类型需与泛型的类型参数类型相同,即为Integer.
Demo<Integer> demo = new Demo <Integer> (123456);
1.泛型的类型参数只能是类类型
2.泛型的类型参数可以有多个
3.如果没有定义具体类型,默认为Object
举个例子吧
!!!比如先定义一个泛型类
package com.ffyc.javacollection.fx;
/*
T:type
E
K
V
支持多个类型的
*/
public class Result<T,E>{
int code;
public T date;//不知道date是什么类型的,泛型
E element;//不知道element是什么类型的,泛型
String message;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public T getDate() {
return date;
}
public void setDate(T date) {
this.date = date;
}
public E getElement() {
return element;
}
public void setElement(E element) {
this.element = element;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
对刚才定义的泛型类进行测试,创建对象
public class Demo2 {
public static void main(String[] args) throws NoSuchFieldException {
//把类型当做参数传递到类中, 类型参数化/参数化类型
Result<String,Integer> result = new Result<>();
result.setDate("abc");
// result.setDate(3); 在编译期间对类型进行检测,只能添加声明的类型
/*
基本类型不能用来声明类型
如果没有声明基本类型,那么类型默认是Object
*/
Result result2 = new Result();
}
● 从泛型类派生子类
子类也是泛型类,子类和父类的泛型类型要一致
public class B<T> extends A<T>
子类不是泛型类,父类要明确泛型的数据类型
public class B extends A<String>
比如,我么定义了一个A类为父类,定义了一个B类为子类:
public class A<T>{
T date;
}
/*
当一个类继承一个泛型类时,将子类也声明为泛型类
*/
public class B<T> extends A<T>
B<String> b = new B();
/*
当一个类继承一个泛型类时,子类不是泛型类,
那就必须明确父类泛型类型
*/
public class B extends A<String>
public class B extends A<String>{
public static void main(String[] args) {
B b = new B();
b.date="'";
}
}
● 泛型接口
泛型接口与泛型类的定义及使用基本相同
public interface Demo { //定义一个泛型接口 }
子类也是泛型类,子类和父类的泛型类型要一致
public class A<T> implements Demo<T>{ }
子类不是泛型类,父类要明确泛型的数据类型
public class A implements Demo <T> { }
● 类型擦除
泛型是Java 1.5版本才引进的概念,在这之前是没有泛型的,但是,泛型代码能够很好地和之前版本的代码兼容。那是因为,泛到信息只存在于代码编译阶段,在进入JVM之前,与泛型相关的信息会被擦除掉,我们称之为一类型擦除。
泛型类被类型擦除后,相应的类型就被替换成 Object 类型或者上限类型.
案例:
public class Result<T,E>{
int code;
public T date;//不知道date是什么类型的,泛型
E element;//不知道element是什么类型的,泛型
String message;
}
Result是一个泛型类,我们查看它在运行时的状态信息可以通过反射。
Result<String,Integer> result3 = new Result<>();
result3.setDate("abc");//虽然声明了泛型类型,但是实际在底层存储时,还是按照Object接收存储,这个称为,类型擦除
Field date = result3.getClass().getField("date");
System.out.println(date.getType());//获取到实际运行时类型
输出:
class java.lang.Object