细说java系列之泛型

什么是范型
简言之,范型是Java支持在编译期进行类型检查的机制。
这里面包含2层含义:其一,可以使用范型进行类型检查;其二,在编译期进行类型检查。
那么,什么叫做在编译期进行类型检查?可以在运行时进行类型检查吗?带着这些疑问,我们一步步深入范型。
范型有什么用途
1. 使用Java集合类时明确指定元素类型

在使用Java提供的集合类时,必须指定具体的类型。举个例子:
// 创建只能存放String类型对象的列表,当存放其他类型的对象时将会编译报错
List<String> strList = new ArrayList<String>();
strList.add("test");
strList.add(1);
当添加非String类型的对象到strList时,编译将报错:
> javac -encoding UTF-8 GenericTest.java
GenericTest.java:27: 错误: 对于add(int), 找不到合适的方法
strList.add(1);
^
方法 Collection.add(String)不适用
(参数不匹配; int无法转换为String)
方法 List.add(String)不适用
(参数不匹配; int无法转换为String)
注: 某些消息已经过简化; 请使用 -Xdiags:verbose 重新编译以获得完整输出
1 个错误
也就是说,通过泛型可以在我们使用Java集合类时,在编译期就避免将不正确的类型添加到集合对象中,而且从语法上就可以很方便地看出集合对象中存放的对象类型,算是一个语法糖。
2. 定义泛型类
首先,我们来看看如果不使用泛型,我们如何解决问题。
假设我们需要定义一个Box类,用于存放对象,通常我们会定义为如下形式(大多数时候我们创建的对象类就是这样):
class Box {
private String obj = null;
public String getObj() {
return obj;
}
public void setObj(String obj) {
this.obj = obj;
}
}
很显然,如果我们实例化一个Box对象,那么这个“盒子”里面只能存放字符串类型的对象。如果强行让它存放其他类型的对象,将会在编译时报错:
Box box = new Box();
//box.setObj("string");
box.setObj(1);
javac -encoding UTF-8 GenericTest.java
GenericTest.java:32: 错误: 不兼容的类型: int无法转换为String
box.setObj(1);
^
注: 某些消息已经过简化; 请使用 -Xdiags:verbose 重新编译以获得完整输出
1 个错误
我们辛辛苦苦(需要编码的哈__)创建了一个“盒子”类,原本是希望可以存放各种各样的对象,结果却只能存放字符串!这是非常不经济的。
如果我们需要存放Integer类型的对象,则需要创建一个新“盒子”类。
当然了,最简单的改造就是让Box类直接存放的对象类型为Object,就可以在Java世界存放所有类型的对象了。
static class Box {
private Object obj = null;
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
}
OK,这时候我们可以任意往“盒子”对象中放任何类型的对象了,既不会在编译时报错,也不会在运行时报错。
Box box = new Box();
//box.setObj("string");
box.setObj(1);
看起来我们已经很完美地解决了问题,但是!当我们从“盒子”里取出对象时,如何知道它是什么类型呢?
当然,我们也是有办法的,因为在Java中可以通过instanceof判断对象类型,而且还可以进行对对象进行强制类型转换。
Box box = new Box();
//box.setObj("string");
box.setObj(1);
Object obj = box.getObj();
if(obj instanceof String) {
System.out.println("object type is String");
String strObj = (String)obj;
System.out.println(strObj);
}else if(obj instanceof Integer) {
System.out.println("object type is Integer");
Integer intObj = (Integer)obj;
System.out.println(intObj);
}
但是,如果采用这样的方式编写代码,会不会太繁琐啦?而且代码的复用性很差。
OK,到这里我们可以看到,不使用泛型,或者Java不支持泛型,我们依然可以干活,但是干活的方式太笨啦!从某种角度讲可能是非常糟糕的方式。
那么,如果使用泛型,会带来什么样的便利呢?
如下所示,将Box声明为泛型类:
static class Box<T> {
private T obj = null;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
}
Box<String> strBox = new Box<String>();
strBox.setObj("string");
String strObj = strBox.getObj();
System.out.println(strObj);
Box<Integer> intBox = new Box<Integer>();
intBox.setObj(1);
Integer intObj = intBox.getObj();
System.out.println(intObj++);
如上所示,当我们把Box声明为泛型类之后,在需要存放指定类型的对象时,只需要在实例化Box对象时明确指定对象类型即可。
另外,当从Box中取出对象后也可以很明确地知道对象类型,不需要对对象进行强制类型转换,最重要的是这使得代码复用非常方便。
3. 定义泛型方法
泛型方法的定义比较特别,需要在方法的返回值类型之前添加泛型标志。
public static <T> void printObj(T obj) {
System.out.println(obj);
}
需要特别注意:在泛型类中定义的方法不一定就是泛型方法!如上Box类中的set/get方法并不是泛型方法。
泛型方法的作用似乎没有泛型类那么大,但是在某些特定的场合使用泛型方法会带来一定的便利。
4. 定义泛型接口
interface Operator<T> {
void printObj(Object obj);
}
使用泛型注意事项
范型仅仅是一种在Java代码进行编译时的静态类型检查机制,是编译时检查。无法在运行时进行类型检查,实际上在运行时已经把类型擦除了。
那为什么对象类型会被擦除?以及存在类型擦除将会带来什么影响?因为存在类型擦除我们在编码时应该注意些什么问题?
详见:https://www.ziwenxie.site/2017/03/01/java-generic/。
【参考】
https://segmentfault.com/a/1190000002646193 Java泛型:泛型类、泛型接口和泛型方法
http://www.jianshu.com/p/b99a40c1f760 浅谈Java泛型
细说java系列之泛型的更多相关文章
- Java系列之泛型
自从 JDK 1.5 提供了泛型概念,泛型使得开发者可以定义较为安全的类型,不至于强制类型转化时出现类型转化异常,在没有反省之前,可以通过 Object 来完成不同类型数据之间的操作,但是强制类型转换 ...
- 细说java系列之反射
什么是反射 反射机制允许在Java代码中获取被JVM加载的类信息,如:成员变量,方法,构造函数等. 在Java包java.lang.reflect下提供了获取类和对象反射信息的相关工具类和接口,如:F ...
- 细说java系列之注解
写在前面 Java从1.5版本之后开始支持注解,通过注解可以很方便地实现某些功能,使用得最普遍的就是Spring框架的注解,大大简化了Bean的配置. 注解仅仅是一种Java提供的工具,并不是一种编程 ...
- 细说java系列之HashMap原理
目录 类图 源码解读 总结 类图 在正式分析HashMap实现原理之前,先来看看其类图. 源码解读 下面集合HashMap的put(K key, V value)方法探究其实现原理. // 在Hash ...
- 夯实Java基础系列13:深入理解Java中的泛型
目录 泛型概述 一个栗子 特性 泛型的使用方式 泛型类 泛型接口 泛型通配符 泛型方法 泛型方法的基本用法 类中的泛型方法 泛型方法与可变参数 静态方法与泛型 泛型方法总结 泛型上下边界 泛型常见面试 ...
- Java系列笔记(5) - 线程
我想关注这个系列博客的粉丝们都应该已经发现了,我一定是个懒虫,在这里向大家道歉了.这个系列的博客是在我工作之余写的,经常几天才写一小节,不过本着宁缺毋滥的精神,所有写的东西都是比较精炼的.这篇文章是本 ...
- Java系列笔记(6) - 并发(上)
目录 1,基本概念 2,volatile 3,atom 4,ThreadLocal 5,CountDownLatch和CyclicBarrier 6,信号量 7,Condition 8,Exchang ...
- 【转】O'Reilly Java系列书籍建议阅读顺序(转自蔡学庸)
Learning Java the O'Reilly's Way (Part I) Java 技术可以说是越来越重要了,不但可以用在计算机上,甚至连电视等家电用品,行动电话.个人数字助理(PDA)等电 ...
- 【细说Java】方法重载的简单介绍
1. 什么是重载 方法名称相同,但它们的参数类型或个数不同,这样,方法在被调用时编译器就可以根据参数的类型与个数的不同加以区分,这就是方法的重载. 既然可以通过参数类型或参数个数来作为重载条件,那返回 ...
随机推荐
- 脚本自动封掉并发数过高的 IP
防止扫描器对服务器恶意扫描,可以对 iptables 规则做了比较严格的配置. 以下配置可作为参考: #lo -A INPUT -i lo -j ACCEPT -A OUTPUT -o lo -j A ...
- 【题解】 bzoj3916: [Baltic2014]friends (字符串Hash)
题面戳我 Solution 首先长度为偶数可以直接判掉 然后我们可以枚举删的位置,通过预处理的\(hash\),判断剩余部分是否划分成两个一样的 判重要注意,我们把字符串分为三个部分\(L_l+1+L ...
- 【BZOJ3215/3216】[ZJOI2013]话旧/话旧2(组合数学,动态规划)
[BZOJ3215/3216][ZJOI2013]话旧/话旧2(组合数学,动态规划) 题面 BZOJ3215 BZOJ3216 题解 先解决\(3216\),求的是最小值为\(0\). 因为起点就是\ ...
- 「九省联考 2018」IIIDX 解题报告
「九省联考 2018」IIIDX 这什么鬼题,送的55分要拿稳,实测有60? 考虑把数值从大到小摆好,每个位置\(i\)维护一个\(f_i\),表示\(i\)左边比它大的(包括自己)还有几个数可以选 ...
- 「SCOI2016」背单词 解题报告
「SCOI2016」背单词 出题人sb 题意有毒 大概是告诉你,你给一堆n个单词安排顺序 如果当前位置为x 当前单词的后缀没在这堆单词出现过,代价x 这里的后缀是原意,但不算自己,举个例子比如abc的 ...
- HEOI2016解题报告
树 在2016年,佳媛姐姐刚刚学习了树,非常开心.现在他想解决这样一个问题:给定一颗有根树(根为1),有以下 两种操作:1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均无标记 ...
- 20165223《JAVA程序设计》第二周学习总结
20165223 <JAVA程序设计>第二周学习总结 教材学习内容总结 第二章要点 标识符与关键字 基本数据类型 类型转换运算 输入输出数据 数组 第三章要点 运算符与表达式 语句概述 i ...
- Typescript学习笔记(四)class 类
typescript的类,与c#,java等语言的类类似.也是包含了一大部分的es6的实现.我会用最通俗的语言讲一下对coding有用的地方. class Greeter { greeting: st ...
- MySQL 之 库操作,表操作
系统数据库 information_schema :虚拟库,不占用磁盘空间,存储的是数据库启动后的一些参数,如用户表信息.列信息.权限信息.字符信息等 mysql:核心数据库,里面包含用户.权限.关键 ...
- python学习笔记:python异常的调用原理
因为错误是class,捕获一个错误就是捕获到该class的一个实例.因此,错误并不是凭空产生的,而是有意创建并抛出的.Python的内置函数会抛出很多类型的错误,我们自己编写的函数也可以抛出错误. h ...