您的位置:首页 > 编程语言 > Java开发

java泛型总结学习

2017-03-17 17:50 344 查看

什么是泛型?

泛型 , 即 “参数化类型” . 提到参数 , 最熟悉的就是定义方法时有形参 , 然后调用方法传递实参 . 那么参数化类型如何理解? 就是将类型由原来具体的类型参数化 , 类似于方法中的变量参数 , 此时类型也定义成参数形式 , 然后在调用的时候传入具体的类型 .

下面采用泛型后, 当通过List<’String> , 就相当于给List传递了参数类型是String, 限定了集合中只能含有String类型元素 , 集合保存了这个类型, 当add(“123”) , 无需进行类型转换 , 因为编译器明确可以传递String类型 . 当然如果传入Integer类型是不会通过编译的 .



java中一种类型 , 被参数化的类型 , 也就是可以通过参数传递类型 . 带参数的类型就是泛型 , 被参数化的类型 .

泛型擦除:

java泛型概念的提出 , 导致泛型只是作用于代码的编译阶段 , 在编译过程中 , 对于正确的检验泛型结果后 , 会将泛型的相关信息擦除 , 也就是说 , 成功编译过后的class文件是不包含任何的泛型信息的 . 泛型信息不会进入运行时阶段 .

泛型其实只是在编译器实现的,而虚拟机并不认识泛型类型,所以要在虚拟机中将泛型类型进行擦除 , 也就是说编译阶段使用泛型 , 运行阶段取消泛型 ,

擦除是将泛型类型由父类代替的 , 如String变成Object , 其实使用的时候还是带着强制的类型转换 , 只不过这是比较安全的转换 , 因为在编译阶段已经确保数据的一致性 .

验证泛型只在编译器有效:

所有反射操作是在运行期, 既然是true, 说明编译之后, 程序会采用去泛型化措施 ,

编译过程中, 正确检验泛型结果后, 会将泛型的相关信息擦除, 并且在对象进入和离开方法的边界处添加类型检查和类型转换方法, 也就是说成功编译后的class方法是不包含任何泛型信息的

public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
List commonList = new ArrayList();
// 说明运行期字节码文件是相同的
System.out.println(stringList.getClass() == commonList.getClass()); // true
}


利用反射跳过泛型检查:

public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("zhangsan");
// 利用泛型进行添加
try{
Class clazz = list.getClass();
Method method = clazz.getMethod("add", Object.class);
// 添加Integer类型数据, 因为反射是在运行期, 说明泛型的检验是在编译期
method.invoke(list, 100);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(list);
}


泛型通配符:

对于任意(引用)类型 T,ArrayList<’?> 类型是ArrayList<’T> 的超类型(类似原始类型 ArrayList 和根类型 Object)。

但是ArrayList<’Object>并不是ArrayList<’String>的超类型.



泛型上下限:

T super Number,那么T就是Number父类级别的类型,

T extends Number 的话,Number是T的父级别类型.



应用:

获取下面test方法的形参中泛型的参数类型.

public class Test {

/**
* 三个参数, 只有两个是泛型类型参数
* @param map
* @param list
* @param str
*/
public void test(Map<String,Integer> map, List<String> list, String str) {
System.out.println("hello world");
}

public static void main(String[] args) {
try {
Method method = Test.class.getMethod
("test", Map.class, List.class, String.class);
// 获取方法的形参
Type[] paramterTypes = method.getGenericParameterTypes();
// 遍历所有类型, 判断是否是泛型的类型
for(Type type : paramterTypes) {
if(type instanceof ParameterizedType) {
Type[] ts = ((ParameterizedType)type).getActualTypeArguments();
for(Type t : ts) {
// 打印结果:String  Integer  String
System.out.println(((Class)t).getSimpleName());
}
}

}
}catch(Exception e) {
e.printStackTrace();
}
}
}


比如在三大框架整合中, 我们需要将所有增删改查操作方法放在父类中, UserService操作的实体类型是User, DepartService操作类型是Depart . 那么如何让父类BaseService获得到子类操作的实体类型(Hibernate中增删改查需要获取实体类型名称) ?

父类:

public class BaseService<T> {

private Class<T> clazz;

@SuppressWarnings("unchecked")
public BaseService() {
// 获取子类的class对象
Class c = (Class<T>) this.getClass();
// 获取子类的extend类型
Type type = c.getGenericSuperclass();
// 判断是否是泛型
if(type instanceof ParameterizedType) {
// 获取泛型的参数类型(默认获取第一个)
Type t = ((ParameterizedType)type).getActualTypeArguments()[0];
clazz = (Class<T>) t;
System.out.println(clazz.getSimpleName());
}
}
}


UserService:

public class UserService extends BaseService<User> {

public static void main(String[] args) {
new UserService(); // User
}
}


DepartService:

public class DepartService extends BaseService<Depart> {

public static void main(String[] args) {
new DepartService(); // Depart
}
}


泛型好处 :

(1) 类型安全

通过泛型定义的变量类型限制, 编译器可以很有效的提高java程序的类型安全 .

(2) 消除强制类型转换

消除强制类型转换, 使得代码更加可读, 并减少出错的机会, 所有的强制转换都是自动
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 泛型