文档章节

Java泛型一览笔录

斯武丶风晴
 斯武丶风晴
发布于 2017/09/27 20:00
字数 1362
阅读 205
收藏 3

1、什么是泛型?

泛型(Generics )是把类型参数化,运用于类、接口、方法中,可以通过执行泛型类型调用 分配一个类型,将用分配的具体类型替换泛型类型。然后,所分配的类型将用于限制容器内使用的值,这样就无需进行类型转换,还可以在编译时提供更强的类型检查。

 

2、泛型有什么用?

泛型主要有两个好处:

(1)消除显示的强制类型转换,提高代码复用

(2)提供更强的类型检查,避免运行时的ClassCastException

 

3、泛型的使用

类型参数(又称类型变量)用作占位符,指示在运行时为类分配类型。根据需要,可能有一个或多个类型参数,并且可以用于整个类。根据惯例,类型参数是单个大写字母,该字母用于指示所定义的参数类型。下面列出每个用例的标准类型参数:

E:元素
K:键
N:数字
T:类型
V:值
S、U、V 等:多参数情况中的第 2、3、4 个类型
?  表示不确定的java类型(无限制通配符类型)

 

4、有界泛型

<? extends T>:是指 “ 上界通配符 (Upper Bounds Wildcards) ”

<? super T>:是指 “ 下界通配符 (Lower Bounds Wildcards) ”

---这里有个坑

举个例子,如下,注释的部分是编译不通过的。

/**
 * @author Sven Augustus
 */
public class Test {

	static class Species{}
	static class Human extends Species{}
	static class Man extends Human{}
	static class Woman extends Human{}

	public static void main(String[] args) {
		List<Human> list = new ArrayList<Human>();
		list.add(new Man());
		list.add(new Woman());
//		Man o11 = (Man) list.get(0); // 这不能保证转型成功,也不是泛型的初衷
		Human o12 = list.get(0);

		List<? extends Human> list2 = new ArrayList<Human>();
//		list2.add(new Object()); // 编译错误:这不能写入元素,类型校验失败
//		list2.add(new Species()); // 编译错误:这不能写入元素,类型校验失败
//		list2.add(new Human()); // 编译错误:这不能写入元素,类型校验失败
//		list2.add(new Man()); // 编译错误:这不能写入元素,类型校验失败
//		list2.add(new Woman()); // 编译错误:这不能写入元素,类型校验失败
//		Man o21 = (Man) list2.get(0);// 这不能保证转型成功,也不是泛型的初衷
		Human o22 = list2.get(0);

		List<? super Human> list3 = new ArrayList<Human>();
//		list3.add(new Object()); // 编译错误:这不能写入元素,类型校验失败
//		list3.add(new Species()); // 编译错误:这不能写入元素,类型校验失败
		list3.add(new Human());
		list3.add(new Man());
		list3.add(new Woman());
//		Man o31 = (Man) list3.get(0); // 这不能保证转型成功,也不是泛型的初衷
//		Human o32 = list3.get(0); // 编译错误:无法自动转型为 Number
		Object o33 = list3.get(0);
	}

}

 

那么我们看到

如 List<? extends T> 大家以为元素为 T以及其所有子类的对象 的List。其实不是。元素类型 仅指T的某一个不确定的子类,是单一的一个不确定类,没有具体哪个类。因此不能插入一个不确定的。

List<? super T> 大家以为元素为 T以及其父类的对象 的List。其实不是,元素类型 仅指T的某一个不确定的父类,是单一的一个不确定类(只确定是T的父类),没有具体哪个类。

因此:

不能往List<? extends T>中插入任何类型的对象。唯一可以保证的是,你可以从中读取到T或者T的子类。

可以往List<? super T>中插入T或者T子类的对象,但不可以插入T父类的对象。可以读取到Object或者Object子类的对象(你并不知道具体的子类是什么)。

 

我们总结一下:

如果频繁支持读取数据,不要求写数据,使用<? extends T>。即生产者 使用 <? extends T>

如果频繁支持写入数据,不特别要求读数据,使用<? super T>。即消费者 使用 <? super T>

如果都需要支持,使用<T>。

 

5、类型擦除

Java的泛型在编译期间,所有的泛型信息都会被擦除掉。

Class c1 = new ArrayList<Integer>().getClass();  
Class c2 = new ArrayList<Long>().getClass();   
System.out.println(c1 == c2); 

这就是 Java 泛型的类型擦除造成的,因为不管是 ArrayList<Integer> 还是 ArrayList<Long>,在编译时都会被编译器擦除成了 ArrayList。Java 之所以要避免在创建泛型实例时而创建新的类,从而避免运行时的过度消耗。

 

6、泛型类型信息

那么,如果我们确实某些场景,如HTTP或RPC或jackson需要获取泛型进行序列化反序列化的时候,需要获取泛型类型信息。

可以参照如下:

package io.flysium.standard.generic;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 *  获取运行时的泛型类型信息
 *
 * @author Sven Augustus
 */
public class Test2 {

	static class ParameterizedTypeReference<T> {
		protected final Type type;

		public ParameterizedTypeReference() {
			Type superClass = this.getClass().getGenericSuperclass();
			//if (superClass instanceof Class) {
	// throw new IllegalArgumentException(
//"Internal error: TypeReference constructed without actual type information");
			//	} else {
				this.type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
			//}
		}

		public Type getType() {
			return type;
		}
	}

	public static void main(String[] args) {
// System.out.println(new ParameterizedTypeReference<String>().getType());
// java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
// 此处会输出报错,因此ParameterizedTypeReference 应不能直接实例化,可以考虑加abstract

		System.out.println(new ParameterizedTypeReference<String>() { }.getType());
// ParameterizedTypeReference 的匿名内部类,可以触发super(),
//即 ParameterizedTypeReference()的构造器逻辑,正常运行
	}

}

 

注意一个关键点:

可以通过定义类的方式(通常为匿名内部类,因为我们创建这个类只是为了获得泛型信息)在运行时获得泛型参数。

 

 

© 著作权归作者所有

斯武丶风晴
粉丝 56
博文 49
码字总数 61098
作品 0
广州
高级程序员
私信 提问
Kotlin 泛型 VS Java 泛型

建议先阅读我的上一篇文章 -- Java 泛型 和 Java 泛型一样,Kotlin 泛型也是 Kotlin 语言中较难理解的一个部分。Kotlin 泛型的本质也是参数化类型,并且提供了编译时强类型检查,实际上也是伪...

JohnnyShieh
2018/06/11
0
0
提给程序员和开发者的 10 道 Java 泛型面试题

关于泛型的面试题在 Java面试中变得越来越常见,因为 Java 5问世已经有相当长的时间了,越来越多的应用已经迁移到Java 5上来了,并且几乎所有新的Java开发工作也都是在Tiger(Java 5的项目代号...

lwei
2013/10/18
13.2K
30
Cannot make a static reference to the non-stati...

今天碰到这样一些错误,Eclipse提示Cannot make a static reference to the non-static type T。代码如下: public class DAOFactory<D extends TemplateDAO<B>, B> {private static Map<Str......

开源中国驻成都办事处
2013/05/30
0
0
Java 泛型详解-绝对是对泛型方法讲解最详细的,没有之一

对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下。 本文参考java 泛型详解、Java中的泛型方法、 java泛型详解 1 概述 泛型在j...

hensemlee
2018/09/23
0
0
Java中的泛型 (上) - 基本概念和原理

下面我们来详细讨论Java中的泛型,虽然泛型的基本思维和概念是比较简单的,但它有一些非常令人费解的语法、细节、以及局限性,内容比较多。 所以我们分为三节,逐步来讨论,本节我们主要来介...

笔记12
2016/10/29
394
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周二乱弹 —— 你一辈子都不可能跟她这么亲近

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @tom_tdhzz :#今日歌曲推荐# 分享George Benson的单曲《Six Play》: 《Six Play》- George Benson 手机党少年们想听歌,请使劲儿戳(这里) ...

小小编辑
今天
765
10
优雅的关闭Spring Boot

优雅的关闭Spring Boot 1、实现 TomcatConnectorCustomizer 接口拿到Tomcat的连接获取 Tomcat连接池 2、实现 ApplicationListener<ContextClosedEvent> 监听服务器关闭事件,注册JVM钩子函数...

sowhat
今天
2
0
Python3-Web开发

简介 Web开发框架 什么是Web框架? Web应用程序框架或简单的Web框架表示一组库和模块,使Web应用程序开发人员能够编写应用程序,而不必担心协议,线程管理等低级细节。 virtualenv是一个虚拟...

wuxinshui
今天
6
0
使用技媒体实践编写发布博客

技媒体实践博客 CSDN OSChina 知乎 简书 思否 掘金 51CTO

晨猫
今天
4
0
Lucene

1、什么是全文检索 数据分类 我们生活中的数据总体分为两种:结构化数据和非结构化数据。 结构化数据:指具有固定格式或有限长度的数据,如数据库,元数据等。 非结构化数据:指不定长或无固...

榴莲黑芝麻糊
昨天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部