一、什么是泛型?

Java泛型设计原则:只要在编译时期没有出现警告,那么运行时期就不会出现ClassCastException异常.

泛型:把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型

参数化类型:把类型当作是参数一样传递

只能是引用类型

相关术语:ArrayList中的E称为类型参数变量

ArrayList中的Integer称为实际类型参数

整个称为ArrayList泛型类型

整个ArrayList称为参数化的类型ParameterizedType

二、为什么需要泛型

早期Java是使用Object来代表任意类型的,但是向下转型有强转的问题,这样程序就不太安全

首先,我们来试想一下:没有泛型,集合会怎么样Collection、Map集合对元素的类型是没有任何限制的。本来我的Collection集合装载的是全部的Dog对象,但是外边把Cat对象存储到集合中,是没有任何语法错误的。

把对象扔进集合中,集合是不知道元素的类型是什么的,仅仅知道是Object。因此在get()的时候,返回的是Object。外边获取该对象,还需要强制转换

有了泛型以后:代码更加简洁【不用强制转换】

程序更加健壮【只要编译时期没有警告,那么运行时期就不会出现ClassCastException异常】

可读性和稳定性【在编写集合的时候,就限定了类型】

2.1有了泛型后使用增强for遍历集合

在创建集合的时候,我们明确了集合的类型了,所以我们可以使用增强for来遍历集合!

//创建集合对象 ArrayList list = new ArrayList<>();

list.add("hello");

list.add("world");

list.add("java");

//遍历,由于明确了类型.我们可以增强forfor (String s : list) {

System.out.println(s);

}

三、泛型基础

3.1泛型类

泛型类就是把泛型定义在类上,用户使用该类的时候,才把类型明确下来....这样的话,用户明确了什么类型,该类就代表着什么类型...用户在使用的时候就不用担心强转的问题,运行时转换异常的问题了。在类上定义的泛型,在类的方法中也可以使用!

/*1:把泛型定义在类上2:类型变量定义在类上,方法中也可以使用*/

public class ObjectTool {

private T obj;

public T getObj() {

return obj;

}

public void setObj(T obj) {

this.obj = obj;

}

}测试代码:

用户想要使用哪种类型,就在创建的时候指定类型。使用的时候,该类就会自动转换成用户想要使用的类型了。

public static void main(String[] args) {

//创建对象并指定元素类型 ObjectTool tool = new ObjectTool<>();

tool.setObj(new String("钟福成"));

String s = tool.getObj();

System.out.println(s);

//创建对象并指定元素类型 ObjectTool objectTool = new ObjectTool<>();

/*** 如果我在这个对象里传入的是String类型的,它在编译时期就通过不了了.*/

objectTool.setObj(10);

int i = objectTool.getObj();

System.out.println(i);

}

3.2泛型方法

前面已经介绍了泛型类了,在类上定义的泛型,在方法中也可以使用.....

现在呢,我们可能就仅仅在某一个方法上需要使用泛型....外界仅仅是关心该方法,不关心类其他的属性...这样的话,我们在整个类上定义泛型,未免就有些大题小作了。定义泛型方法....泛型是先定义后使用的

//定义泛型方法..public void show(T t) {

System.out.println(t);

}测试代码:

用户传递进来的是什么类型,返回值就是什么类型了

public static void main(String[] args) {

//创建对象 ObjectTool tool = new ObjectTool();

//调用方法,传入的参数是什么类型,返回值就是什么类型 tool.show("hello");

tool.show(12);

tool.show(12.5);

}

3.3泛型类派生出的子类

前面我们已经定义了泛型类,泛型类是拥有泛型这个特性的类,它本质上还是一个Java类,那么它就可以被继承

那它是怎么被继承的呢??这里分两种情况子类明确泛型类的类型参数变量

子类不明确泛型类的类型参数变量

3.3.1子类明确泛型类的类型参数变量泛型接口

/*把泛型定义在接口上*/

public interface Inter {

public abstract void show(T t);

}实现泛型接口的类.....

/*** 子类明确泛型类的类型参数变量:*/

public class InterImpl implements Inter {

@Override

public void show(String s) {

System.out.println(s);

}

}

3.3.2子类不明确泛型类的类型参数变量当子类不明确泛型类的类型参数变量时,外界使用子类的时候,也需要传递类型参数变量进来,在实现类上需要定义出类型参数变量

/*** 子类不明确泛型类的类型参数变量:* 实现类也要定义出类型的**/

public class InterImpl implements Inter {

@Override

public void show(T t) {

System.out.println(t);

}

}

测试代码:

public static void main(String[] args) {

//测试第一种情况 //Inter i = new InterImpl(); //i.show("hello");

//第二种情况测试 Inter ii = new InterImpl<>();

ii.show("100");

}

值得注意的是:实现类的要是重写父类的方法,返回值的类型是要和父类一样的!

类上声明的泛形只对非静态成员有效

3.4类型通配符

为什么需要类型通配符????我们来看一个需求.......

现在有个需求:方法接收一个集合参数,遍历集合并把集合元素打印出来,怎么办?按照我们没有学习泛型之前,我们可能会这样做:

public void test(List list){

for(int i=0;i

System.out.println(list.get(i));

}

}

上面的代码是正确的,只不过在编译的时候会出现警告,说没有确定集合元素的类型....这样是不优雅的...那我们学习了泛型了,现在要怎么做呢??有的人可能会这样做:

public void test(List list){

for(int i=0;i

System.out.println(list.get(i));

}

}

这样做语法是没毛病的,但是这里十分值得注意的是:该test()方法只能遍历装载着Object的集合!!!

强调:泛型中的并不是像以前那样有继承关系的,也就是说List和List是毫无关系的!!!!

那现在咋办???我们是不清楚List集合装载的元素是什么类型的,List这样是行不通的........于是Java泛型提供了类型通配符 ?

所以代码应该改成这样:

public void test(List> list){

for(int i=0;i

System.out.println(list.get(i));

}

}

?号通配符表示可以匹配任意类型,任意的Java类都可以匹配.....

现在非常值得注意的是,当我们使用?号通配符的时候:就只能调对象与类型无关的方法,不能调用对象与类型有关的方法。

记住,只能调用与对象无关的方法,不能调用对象与类型有关的方法。因为直到外界使用才知道具体的类型是什么。也就是说,在上面的List集合,我是不能使用add()方法的。因为add()方法是把对象丢进集合中,而现在我是不知道对象的类型是什么。

3.4.1设定通配符上限

首先,我们来看一下设定通配符上限用在哪里....

现在,我想接收一个List集合,它只能操作数字类型的元素【Float、Integer、Double、Byte等数字类型都行】,怎么做???

我们学习了通配符,但是如果直接使用通配符的话,该集合就不是只能操作数字了。因此我们需要用到设定通配符上限

List extends Number>

上面的代码表示的是:List集合装载的元素只能是Number的子类或自身

public static void main(String[] args) {

//List集合装载的是Integer,可以调用该方法 List integer = new ArrayList<>();

test(integer);

//List集合装载的是String,在编译时期就报错了 List strings = new ArrayList<>();

test(strings);

}

public static void test(List extends Number> list) {

}

3.4.2设定通配符下限

既然上面我们已经说了如何设定通配符的上限,那么设定通配符的下限也不是陌生的事了。直接来看语法吧

//传递进来的只能是Type或Type的父类 super Type>

设定通配符的下限这并不少见,在TreeSet集合中就有....我们来看一下

public TreeSet(Comparator super E> comparator) {

this(new TreeMap<>(comparator));

}

那它有什么用呢??我们来想一下,当我们想要创建一个TreeSet类型的变量的时候,并传入一个可以比较String大小的Comparator。

那么这个Comparator的选择就有很多了,它可以是Comparator,还可以是类型参数是String的父类,比如说Comparator....

这样做,就非常灵活了。也就是说,只要它能够比较字符串大小,就行了

值得注意的是:无论是设定通配符上限还是下限,都是不能操作与对象有关的方法,只要涉及到了通配符,它的类型都是不确定的!

经评论补充:在泛型的上限和下限中有一个原则:PECS(Producer Extends Consumer Super)

书上是这样写的:带有子类限定的可以从泛型读取【也就是--->(? extend T)】-------->Producer Extends

带有超类限定的可以从泛型写入【也就是--->(? super T)】-------->Consumer Super

也有相关博文写得很好:

3.5通配符和泛型方法

大多时候,我们都可以使用泛型方法来代替通配符的.....

//使用通配符public static void test(List> list) {

}

//使用泛型方法public void test2(List t) {

}

上面这两个方法都是可以的.....那么现在问题来了,我们使用通配符还是使用泛型方法呢??

原则:如果参数之间的类型有依赖关系,或者返回值是与参数之间有依赖关系的。那么就使用泛型方法

如果没有依赖关系的,就使用通配符,通配符会灵活一些.

3.6泛型擦除

泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛形的java程序后,生成的class文件中将不再带有泛形信息,以此使程序运行效率不受到影响,这个过程称之为“擦除”。

3.6.1兼容性

JDK5提出了泛型这个概念,但是JDK5以前是没有泛型的。也就是泛型是需要兼容JDK5以下的集合的。

当把带有泛型特性的集合赋值给老版本的集合时候,会把泛型给擦除了。

值得注意的是:它保留的就类型参数的上限。

List list = new ArrayList<>();

//类型被擦除了,保留的是类型的上限,String的上限就是Object List list1 = list;

如果我把没有类型参数的集合赋值给带有类型参数的集合赋值,这又会怎么样??

List list = new ArrayList();

List list2 = list;

它也不会报错,仅仅是提示“未经检查的转换”

四、泛型的应用

当我们写网页的时候,常常会有多个DAO,我们要写每次都要写好几个DAO,这样会有点麻烦。

那么我们想要的效果是什么呢??只写一个抽象DAO,别的DAO只要继承该抽象DAO,就有对应的方法了。

要实现这样的效果,肯定是要用到泛型的。因为在抽象DAO中,是不可能知道哪一个DAO会继承它自己,所以是不知道其具体的类型的。而泛型就是在创建的时候才指定其具体的类型。抽象DAO

public abstract class BaseDao {

//模拟hibernate....private Session session;

private Class clazz;

//哪个子类调的这个方法,得到的class就是子类处理的类型(非常重要)public BaseDao(){

Class clazz = this.getClass(); //拿到的是子类 ParameterizedType pt = (ParameterizedType) clazz.getGenericSuperclass(); //BaseDao clazz = (Class) pt.getActualTypeArguments()[0];

System.out.println(clazz);

}

public void add(T t){

session.save(t);

}

public T find(String id){

return (T) session.get(clazz, id);

}

public void update(T t){

session.update(t);

}

public void delete(String id){

T t = (T) session.get(clazz, id);

session.delete(t);

}

}继承抽象DAO,该实现类就有对应的增删改查的方法了。

CategoryDao

public class CategoryDao extends BaseDao {

}

BookDao

public class BookDao extends BaseDao {

}

五、最后

泛型的基础就介绍到这里了,如果以后有需要的话再进行深入研究吧~如果觉得该文章帮助到你,不妨点个赞,关注公众号一波~

参考资料:Core Java如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以关注微信公众号:Java3y

java泛型有什么用_什么叫泛型?有什么作用?相关推荐

  1. 不能使用泛型的形参创建对象_数据类型之----泛型

    泛型 泛型:可以在类或方法中预支地使用未知的类型. 一般在创建对象时,将未知的类型确定具体的类型.当没有指定泛型时,默认类型为Object类型. 使用泛型的好处 <1>将运行时期的Clas ...

  2. 下列关于java泛型的说法正确_下列关于泛型的说法不正确的一项是:( )_学小易找答案...

    [简答题]请将审计实训任务最终的完成情况拍照上传,注意页面上要有自己的名字. [其它]找出点.线.面.体,不同的立体造型各3种,并注明它们材料.颜色.特点.表达的效果等.制作成PPT. [其它]作业: ...

  3. 2021-06-19复习java Collection集合 Iterator接口_迭代器 增强for循环 泛型

    2021-06-19复习java Collection集合 Iterator接口_迭代器 增强for循环 泛型 Collection集合 java.util.coLlection接口 所有单列集合的最 ...

  4. java 泛型和类型擦除_关于Java泛型和擦除

    java 泛型和类型擦除 "编译期间擦除泛型"是常识(好吧,类型参数和实参实际上是被擦除的). 这是由于"类型擦除"而发生的. 但这是错误的,正如许多开发人员所 ...

  5. 【Java 泛型】使用上下边界通配符解决泛型擦除问题

    文章目录 前言 一.使用上边界通配符示例 二.分析字节码的附加信息 前言 上一篇博客 [Java 泛型]泛型用法 ( 泛型编译期擦除 | 上界通配符 <? extends T> | 下界通 ...

  6. Java编程思想 第十五章:泛型

    1. 泛型 "泛型"意思就是适用于许多类型. 使用泛型的目的之一: 指定容器持有什么类型,让编译器确保正确性,而不是在运行期发现错误. 这个容器可以看成是有其他类型对象作为成员的类 ...

  7. Java集合(八) 迭代器Iterator、泛型、Map映射

    一. 迭代器 迭代器的增删作用于原集合 用迭代器遍历集合 , 用指针的挪动来获取对应的元素,通过标记这个元素是否可以用来操作 去操作原集合. 在迭代过程中不允许直接操作原集合. forEach --- ...

  8. Java 中,类、类对象、泛型之间的转换

    Java 中,类.类对象.泛型之间的转换 R 为非泛型 获得类 通过类型名来获得类 通过对象来获得类 通过类名字符串来获得类 通过类来获得类名字符串 通过类来获得对象 使用 R 的无参数构造器来创建对 ...

  9. Java面试知识点:集合、Set、泛型、HashSet、HashMap

    Java面试知识点:集合.Set.泛型.HashSet.HashMap 答案: 1.集合  集合和数组的对比小结      1,数组的长度是不可变的,集合的长度是可变的.      2 ,数组可以存 ...

最新文章

  1. Ubuntu下安装cmake,配置ITK 和 SimpleITK, VTK(已测试可执行)
  2. 如何使用MySQL和JPA使用Spring Boot构建Rest API
  3. Delphi 延迟函数 比sleep 要好的多
  4. 路飞学城Python-Day96
  5. Builder和Factory模式区别
  6. 单元格 编辑 获取_Excel批量导入图片,还能一键将图片固定到单元格!这是什么操作.........
  7. linux audio device driver音频设备驱动
  8. python实时曲线绘制_python画曲线
  9. “四大神兽”拆机指北
  10. linux tpp模式,tpp 'exec'命令任意代码执行漏洞
  11. 智能家居更智能,小程序的生态合作新方案
  12. 智商黑洞(门萨Mensa测试)9
  13. 数字通信系统中的频带利用率
  14. 网页左右两边加广告横幅
  15. 利用pearcmd.php本地文件包含(LFI)
  16. jquery 获取元素中class值
  17. 【2022 小目标检测综述】Towards Large-Scale Small Object Detection: Survey and Benchmarks
  18. c语言实用计算器项目,C语言实现简单计算器小项目
  19. 【Spring】Spring 学习
  20. DDD-领域驱动设计入门

热门文章

  1. 福利,剪映PC版来了~ 支持windos系统和苹果系统
  2. {转载}java Finally块中代码什么时候执行
  3. 钉钉合种,来至善林一周解锁蚂蚁森林全部大树图标
  4. 怎样解除计算机休眠状态,电脑怎么取消休眠【设置模式】
  5. linux 磁盘并发io,Linux系统 磁盘IO过高排查总结
  6. 如何用python 300行代码实现中医问诊AI
  7. c++求数组长度 和vector用法
  8. textview长按复制_android-TextView的长按复制
  9. 2019年第三期全国高校大数据与人工智能骨干师资研修班
  10. FATFS 库学习笔记(一)