泛型就这么简单

C#泛型、使用泛型构建简单的二叉树类、泛型方法、可变性和泛型接口、协变接口、逆变接口 泛型解决方案 C# 通过泛型避免强制类型转换,增强类型安全性,减少装箱量.泛型类和方法接受类型参数.它们指定了要操作的对象的类型 如下所示: class Queue<T> { ... } T就是类型参数,作为占位符使用,会在编译时被真正的类型取代.写代码实例化泛型Queue时,需要指定用于取代T的类型,在类中定义字段和方法时,也可以用同样的占位符指定这些项的类型. 类型参数不一定... 阅读详情

前言

从今天开始进入Java基础的复习,可能一个星期会有一篇的<十道简单算法>,我写博文的未必都是正确的~如果有写错的地方请大家多多包涵并指正~

今天要复习的是泛型,泛型在Java中也是个很重要的知识点,本文主要讲解基础的概念,并不是高深的知识,如果基础好的同学可以当复习看看~

一、什么是泛型?

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

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

参数化类型:

  • 把类型当作是参数一样传递
  • <数据类型> 只能是引用类型

相关术语:

  • ArrayList<E>中的E称为类型参数变量
  • ArrayList<Integer>中的Integer称为实际类型参数
  • 整个称为ArrayList<E>泛型类型
  • 整个ArrayList<Integer>称为参数化的类型ParameterizedType

二、为什么需要泛型

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

首先,我们来试想一下:没有泛型,集合会怎么样

  • Collection、Map集合对元素的类型是没有任何限制的。本来我的Collection集合装载的是全部的Dog对象,但是外边把Cat对象存储到集合中,是没有任何语法错误的。
  • 把对象扔进集合中,集合是不知道元素的类型是什么的,仅仅知道是Object。因此在get()的时候,返回的是Object。外边获取该对象,还需要强制转换

有了泛型以后:

  • 代码更加简洁【不用强制转换】
  • 程序更加健壮【只要编译时期没有警告,那么运行时期就不会出现ClassCastException异常】
  • 可读性和稳定性【在编写集合的时候,就限定了类型】

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

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

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

        list.add("hello");
        list.add("world");
        list.add("java");

        //遍历,由于明确了类型.我们可以增强for
        for (String s : list) {
            System.out.println(s);
        }

三、泛型基础

3.1泛型类

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

  • 在类上定义的泛型,在类的方法中也可以使用!

/*
    1:把泛型定义在类上
    2:类型变量定义在类上,方法中也可以使用
 */
public class ObjectTool<T> {
    private T obj;

    public T getObj() {
        return obj;
    }

    public void setObj(T obj) {
        this.obj = obj;
    }
}
  • 测试代码:

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


    public static void main(String[] args) {
        //创建对象并指定元素类型
        ObjectTool<String> tool = new ObjectTool<>();

        tool.setObj(new String("钟福成"));
        String s = tool.getObj();
        System.out.println(s);


        //创建对象并指定元素类型
        ObjectTool<Integer> objectTool = new ObjectTool<>();
        /**
         * 如果我在这个对象里传入的是String类型的,它在编译时期就通过不了了.
         */
        objectTool.setObj(10);
        int i = objectTool.getObj();
        System.out.println(i);
    }

3.2泛型方法

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

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

  • 定义泛型方法….泛型是先定义后使用的

    //定义泛型方法..
    public <T> 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类,那么它就可以被继承

那它是怎么被继承的呢??这里分两种情况

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

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

  • 泛型接口

/*
    把泛型定义在接口上
 */
public interface Inter<T> {
    public abstract void show(T t);

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

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

public class InterImpl implements Inter<String> {
    @Override
    public void show(String s) {
        System.out.println(s);

    }
}

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

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

/**
 * 子类不明确泛型类的类型参数变量:
 *      实现类也要定义出<T>类型的
 *
 */
public class InterImpl<T> implements Inter<T> {

    @Override
    public void show(T t) {
        System.out.println(t);

    }
}

测试代码:

    public static void main(String[] args) {
        //测试第一种情况
        //Inter<String> i = new InterImpl();
        //i.show("hello");

        //第二种情况测试
        Inter<String> ii = new InterImpl<>();
        ii.show("100");

    }

值得注意的是:

  • 实现类的要是重写父类的方法,返回值的类型是要和父类一样的!
  • 类上声明的泛形只对非静态成员有效

3.4类型通配符

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

现在有个需求:方法接收一个集合参数,遍历集合并把集合元素打印出来,怎么办?

  • 按照我们没有学习泛型之前,我们可能会这样做:

public void test(List list){


    for(int i=0;i<list.size();i++){

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

    }
}

上面的代码是正确的,只不过在编译的时候会出现警告,说没有确定集合元素的类型….这样是不优雅的…

  • 那我们学习了泛型了,现在要怎么做呢??有的人可能会这样做:

public void test(List<Object> list){


    for(int i=0;i<list.size();i++){

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

    }
}

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

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

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

所以代码应该改成这样:


public void test(List<?> list){


    for(int i=0;i<list.size();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> integer = new ArrayList<>();
        test(integer);

        //List集合装载的是String,在编译时期就报错了
        List<String> 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>类型的变量的时候,并传入一个可以比较String大小的Comparator。

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

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

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

3.5通配符和泛型方法

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


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

    }

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

    }

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

原则:

  • 如果参数之间的类型有依赖关系,或者返回值是与参数之间有依赖关系的。那么就使用泛型方法
  • 如果没有依赖关系的,就使用通配符,通配符会灵活一些.

3.6泛型擦除

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

3.6.1兼容性

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

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

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


        List<String> list = new ArrayList<>();

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

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


        List list = new ArrayList();
        List<String> list2 = list;

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


四、泛型的应用

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

这里写图片描述

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

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

  • 抽象DAO


public abstract class BaseDao<T> {

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


    //哪个子类调的这个方法,得到的class就是子类处理的类型(非常重要)
    public BaseDao(){
        Class clazz = this.getClass();  //拿到的是子类
        ParameterizedType  pt = (ParameterizedType) clazz.getGenericSuperclass();  //BaseDao<Category>
        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<Category> {

}

BookDao


public class BookDao extends BaseDao<Book> {


}

五、最后

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

参考资料:

  • Core Java

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

【unity】简单的泛型单例类 这样,当你需要访问 MySingletonClass 的实例时,只需要调用 MySingletonClass.Instance 就可以了。如果此时实例还没有创建,代码会自动创建一个。同时,每个继承 Singleton 的类都有机会在实例创建时执行自己的初始化方法(重写 Init() 即可)。 阅读详情

相关推荐

Postman调试海康ISAPI接口全记录:从鉴权失败到成功修改设备名的踩坑指南

本文详细记录了使用Postman调试海康ISAPI接口的全过程,从鉴权失败到成功修改设备名的实战经验。涵盖Digest认证机制、XML请求体构造、常见错误排查及代码实现过渡,帮助开发者高效解决海康设备集成中的各种问题。

weixin_42620913的博客 661

泛型基础【介绍泛型、泛型类、泛型方法、通配符、擦除】

什么是泛型? Java泛型设计原则:只要在编译时期没有出现警告,那么运行时期就不会出现ClassCastException异常 泛型:把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型 参数化类型: 把类型当作是参数一样传递 &lt;数据类型&gt; 只能是引用类型 相关术语: ArrayList&lt;E&gt;中的E称为类型参数变量 ArrayList&...

3y 500

FPGA资料分享——FPGA开发板入手指南

FPGA学习,开发板怎么选,看这里,就一个通透

weixin_51627076的博客 7297

泛型总结(通俗易懂)

泛型 1,什么是泛型 泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢? 顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参), 然后在使用/调用时传入具体的类型(类型实参)。 泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分

qq_52880445的博客 2万+

半导体设备微振动控制:天棚阻尼PID与SIMULINK建模实战

半导体制造设备对微振动的敏感度已进入纳米级,光刻机、量测设备等一旦遭遇超过VC-C振动准则的扰动,便会出现套刻误差或检测误判。主动隔振技术通过传感器、控制器与执行器形成闭环,能有效抑制低频共振,其中天棚阻尼控制因物理概念清晰、鲁棒性强而成为工程首选。结合MATLAB/SIMULINK建模,可在设计阶段完成系统性能边界评估与参数整定,再通过C代码生成与FMU导出实现算法向控制器和跨平台仿真迁移。本文从VC-C指标拆解出发,依次讲解天棚阻尼PID控制原理、两自由度被控对象建模、控制增益估算、仿真验证方法,并给出

weixin_29169505的博客 326

C#+Winform+OpenCvSharp实战:5步搞定工业视觉检测框架(附源码下载)

本文详细介绍了如何使用C#、Winform和OpenCvSharp构建工业视觉检测框架,通过5个关键步骤实现高效、稳定的视觉检测系统。内容涵盖环境配置、相机集成、核心算法实现、系统集成与通信,以及部署优化,帮助开发者快速掌握工业视觉检测技术,提升检测精度和效率。附完整源码下载,助力工业数字化转型。

pz89012345的博客 551

什么是泛型?

泛型:就是指在类定义时不会设置类中的属性或方法参数的具体类型,而是在类使用时(创建对象)再进行类型的定义。会在编译期检查类型是否错误。

m0_49991895的博客 3万+

硬件工程师必备核心知识点

硬件设计是一项系统化工程活动,涉及元器件选型、电源设计、信号完整性、EMC和PCB工艺等多个环节

硬核科技 338

Java 中的泛型(超全详解)

泛型的出现就是为了统一集合当中数据类型的尖括号 <> 中的 泛型标识被称作是类型参数,用于指代任何数据类型。T :代表一般的任何类。E :代表 Element 元素的意思,或者 Exception 异常的意思。K :代表 Key 的意思。V :代表 Value 的意思,通常与 K 一起配合使用。S :代表 Subtype 的意思,文章后面部分会讲解示意。自己实现集合/*E: 表示不确定的类型,该类型在类名后面已经定义过了e: 形参的名字,变量名*/

weixin_65752158的博客 3万+

OpenMM | PDBFixer 用于蛋白质预处理

PDBFixer 用于蛋白质预处理

DrugOne 3006

基于语义分割的实时建图:HDMap动态更新

基于语义分割的实时建图:HDMap动态更新​ ,人工智能,计算机视觉,大模型,AI,本文围绕基于语义分割的实时建图技术在HDMap动态更新中的应用展开,介绍了语义分割基础、HDMap概述、相关实时建图技术、动态更新关键技术、系统性能评估指标、实际应用案例、面临的挑战与解决方案及未来发展趋势。阐述了该技术如何利用传感器数据和语义分割算法,解决传统HDMap构建与更新的弊端,分析了其在精度、实时性、鲁棒性等方面的表现,以及在自动驾驶和智能交通中的应用,还探讨了现存问题及未来优化方向。

不踩坑,不知道坑有多深;不总结,踩过的坑白踩。 22万+

Java泛型简单的使用

Java泛型简单的使用 前言 Java里面的泛型在实际开发中运用的很多,学过C++的同学一定知道C++的模板,而Java中的泛型,一定程度上和它还是挺像的。 相信写Java的人,大都有用过List的实现类ArrayList。在Java没有泛型之前,它的内部是一个Object的数组实现的。这也导致一个问题,每次使用里面的元素的时候需要向下转型,而且很明显,如果是Object的话,意味着我们可以丢任何...

java_jiagoushi的博客 220

MicroCap声学仿真介绍(一)

5-频率上下限选择,需要注意的是,第一个数字是上限,第二个数字是下限,如果填反了,会有报错提示;下三角可以选择“Auto”、“linear”、“log”和“list”,设置的是频率点之间的距离和频率点的数量。Component是用来选择部件的模块,如上图所示,声学仿真常用到的是“Electroacoustic Macros AC”、“Electroacoustic Macros TR”、“NXP Transducers”以及“NXP HD components”。3是声学部件,是声学仿真中常用的部件。

山无忧的博客 5095

设计模式之从简单工厂到泛型简单工厂

设计模式之简单工厂模式到泛型工厂          简单工厂模式(Simple Factory Pattern)又称为虽然23种设计模式中并不包含简单工厂模式,但是它是一种创建性模式应用的也相对比较广泛。在简单工厂模式中,可以根据参数的不同返回不同类的实例,被创建的实例通常都有相同的父类。         以下是该模式的模式结构图:         首先我们称创建的实例为产品     ...

这是要成为全栈的节奏么 821

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

泛型(1):简单泛型

一般的类和方法,只能使用具体的类型:要么是基本类型,要么是自定义的类,如果要编写可以应用于多种类型的代码,这种刻板的限制对代码的束缚就会很大。在面向对象编程语言中,多态算是一种泛化机制。例如,你可以将方法的参数类型设为基类,那么该方法就可以接受从这个基类中导出的任何类作为参数。这样的方法更加通用一些,可应用的地方也多一些。在类的内部也是如此,凡是需要说明类型的地方,如果都使用基类,确实能够具备更好的灵活性。

游王子og的博客 499

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

简单认识泛型

方法限定符 <类型形参列表> 返回值类型 方法名称(形参列表) { ... }

2301_80720675的博客 820

Java 泛型设计(泛型简单定义、类型擦除)与泛型使用的局限性

从Java 5引入。泛型程序设计意味着编写的代码可以对多种不同类型的对象重用。 在Java中增加泛型类之前,泛型程序设计是用继承实现的。ArrayList类只维护一个 Object 引用的数组。 定义简单泛型类 public class Pair<T> { private T first; private T second; ... ... } 泛型方法 class ArrayArg { public static <T> T getM.

一只青年痴呆 1272

JAVA中泛型的简单样例

@简单的泛型样例 泛型的功能 类似于在使用之前用符号占用了一个类型选项,后面使用需要具体到哪种类型,有点类似形参和实参,常用的符号有E、T、V public class TestGeneric { public static void main(String[] args) { Collection col = new Collection<>(); col.set(“模拟”, 1); col.set(“张三”, 2); System.out.println(col.get(1)); } } c

Dragon__Wu的博客 209

java泛型的常用,java泛型(一)--泛型的簡單介紹以及常用情況

一.泛型的基本概念泛型是JavaSE5引入的一個新概念,實現了參數化類型的概念,並且使代碼可以應用於多種類型。在日常編寫程序時,我們都會很注重“泛化”,像多態其實就是一種類型的泛化,將子類的對象賦給父類的引用獲得更好的泛化特性,將方法的參數類型設置為父類以獲得泛化特性等等...... 同樣,為類聲明出接口,而后對接口進行操作同樣也可以獲得很好的泛化特性。但是上述的方法都有一定的局限性,那就是繼承...

weixin_39624873的博客 126

泛型简单理解

什么是泛型?   泛型,我的简单理解为是所有类型的抽象类,任何类型都是泛型的子类。   例如:假如让你做一个最简单的方法,把传入的int类型的数字进行打印,你也许会这样做: public void print(int input) { Console.Write("输入的是" + input); }   如果再让你添加一个方法,

郭新宇 天道酬勤 701

关于泛型的简单认识

引入泛型的前置知识:我们都知道Object类是所有类的父类,那么我们创建一个Object数组的时候,我们是否能够往这个数组里传入不同类型的数据呢?由上述代码截图可知,我们确实能成功的往一个Object类型的数组里面添加不同类型的数据,但是此时我们想要从Object类型的数组里面去数据的时候,程序却出现了编译错误,那么,这是为什么呢?原因很简单,就是我们在取数据的时候要进行类型转换,因为getVal方法返回给我们的是一个Object类型的数据,所以我们取数据的时候要进行强制类型转换。

2202_75713447的博客 1079
上一篇: 【Java EE】从零开始写项目【总结】
下一篇: 注解就这么简单
Java3y
Java3y 领域专家: 后端开发技术领域 领域专家: 后端开发技术领域
博客等级 码龄9年 1万+粉丝 509原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值