JavaSE基础篇:泛型说明

news2024/9/27 8:10:01

本文整理自B站:JavaSE强化教程泛型,由点到面的讲解了整个泛型体系

  • 第一章:泛型概述
    • 一:泛型概念
    • 二:泛型类
    • 1:泛型类使用
    • 2:泛型类派生子类
      • 1):子类也是泛型类
      • 2):子类不是泛型类
    • 三:泛型接口
    • 1:实现类不是泛型类
    • 2:实现类也是泛型类
    • 四:泛型方法
    • 1:概念
    • 2:具体使用
    • 3:静态泛型方法
    • 4:泛型方法与可变参数
  • 第二章:泛型通配符
    • 一:什么是类型通配符
    • 二:类型通配符上限
    • 三:类型通配符下限
  • 第三章:类型擦除
    • 一:无限制类型擦除
    • 二:有限制类型擦除
    • 三:擦除方法中类型参数
    • 四:桥接方法
  • 第四章:泛型与数组
    • 一:带泛型数组的对象不可以直接创建
    • 二:通过反射创建泛型的数组
  • 第五章:泛型与反射
    • 一:反射中常用的泛型类
    • 二:泛型对反射的好处

第一章:泛型概述

一:泛型概念

JAVA推出泛型以前,程序员可以构建一个元素类型为Object的集合,该集合能够存储任意的数据类型对象,而在使用该集合的过程中,需要程序员明确知道存储每个元素的数据类型,否则很容易引发ClassCastException异常。

Java泛型(generics)是JDK5中引入的一个新特性,泛型提供了编译时类型安全监测机制,该机制允许我们在编译时检测到非法的类型数据结构
泛型的本质就是参数化类型,也就是所操作的数据类型被指定为一个参数。

二:泛型类

在这里插入图片描述
泛型标识可以理解称为一个类型的形参,我们拿着这个泛型标识可以去声明泛型变量+方法的返回值类型和参数类型。

常用的泛型标识T、E、K、V其中KV都是结合使用。

1:泛型类使用

package com.dashu.nettytest;

public class Generic<T> {

    private T t;

    public Generic(T t){
        this.t = t;
    }

    public T getT(){
        return t;
    }

    public void setT(T t){
        this.t = t;
    }
    
}

这里的泛型就是一个泛型标识,它做了成员变量的类型,普通方法的入参和出参,构造方法的入参。

我们在创建对象的时候来指定T的具体类型,假如我们传入的是String类型,那么就说明我们要在这个类里边玩String类型了。

这里T是有外部使用类的时候传入的。

在这里插入图片描述
创建泛型类对象时如果并没有指定泛型,那么这里边的泛型将会按照Object类型来进行操作。

泛型类不支持基本数据类型,仅仅支持引用类型。我们真正使用T的时候,是转换成了Object,在使用类里边的泛型成员的时候,会在一个适当的机会将Object传递过来的数据类型转换为适当的类型,而我们的基本数据类型并不是继承自Object,玩具发进行转换。

使用泛型之后的对象的Class对象也都是一个。他们的Class对象的地址值也是一个。

2:泛型类派生子类

在这里插入图片描述

1):子类也是泛型类

这里我们定义的父类,我们使用E作为泛型标志。

package com.dashu.nettytest;
public class Parent<E> {
    private E value ;

    public E getValue(){
        return this.value;
    }
    
    public void setValue(E e){
        this.value = e;
    }
}

父类是泛型类,子类也是泛型类的这种情况,子类和父类的泛型标志要一样来标识子类和父类是同一个泛型。

假如我们在这里不给父类声明泛型变量的话:

public class Son<T> extends Parent {
    public Object getValue(){
        return super.getValue();
    }
}

不给父类声明泛型变量,那么他默认就是Object那么我们使用开发工具将父类的getValue()方法重写之后就返回的是Object类型的变量。
假如我们给父类声明变量但是父子泛型变量不一致的话:

public class Son<T> extends Parent<E> {
    public E getValue(){
        return super.getValue();
    }
}

就会发生下面的问题:
在这里插入图片描述
所以,子类继承泛型父类的时候,父子类要是都使用泛型功能的的话,标志必须要一致。这样做的话也是好理解的,我们创建子类对象的时候,必须先去创建父类对象,创建父类对象的话就会把泛型标识传递给父类。当然,子类泛型和父类泛型一致即可,我们也可以在子类的泛型当中进行扩展,只要保证父类的泛型标志在子类当中都有即可。

2):子类不是泛型类

子类不是泛型类,父类必须明确泛型的数据类型

public class Son extends Parent<Integer>{
}

我们如果定义的时候这样定义就会报错:
在这里插入图片描述
编译报错原因就是必须要指定具体的引用类型,而且这里也明确提示了,请创建E这个类,说明编译器在这里也认为E是一个具体类型,但是没有被创建而已。

三:泛型接口

实现类不是泛型类,接口要明确数据类型
实现类也是泛型类,实现类和接口的泛型类型要一致。

声明泛型接口和普通接口是一样的,声明泛型接口,添加的泛型可以作为方法的入参和返回值。

1:实现类不是泛型类

package com.dashu.nettytest;

public interface Generator<T> {
    T getKey();
}
class Apple implements Generator {
    @Override
    public Object getKey() {
        return null;
    }
}
class Banana implements Generator<String> {
    @Override
//    public Object getKey() {
//指定了泛型类型为String类型之后,这里不能返回Object了,重写返回类型要一致。    
    public String getKey() {
        return null;
    }
}

子类不是一个泛型类,必须指明接口泛型,如果不指明的话默认就是Object。

2:实现类也是泛型类

子类是泛型类的话,父类如果也要使用泛型类的话,父子类型需要一致。当然子类可以拓展,但是必须包含父类的泛型标志。

四:泛型方法

泛型类是在实例化类的时候,指明泛型的具体类型
泛型方法是在调用方法的时候指明泛型的具体类型。

1:概念

我们上述案例中的方法不是泛型方法,只不过是一个普通的成员方法,只不过类型使用了泛型类的类型。

在这里插入图片描述
修饰符后边和返参结果之前的那个区域叫做泛型列表,只有声明了这个区域的方法才叫泛型方法。

方形类中使用了泛型的成员方法并不是泛型方法。

表明该方法将使用泛型类型T,此时才可以在方法汇总使用泛型类型T

T可以为任意标识,常见的就是T、E、K、V

2:具体使用

class Test{
    public <B> B getArrayList(ArrayList<B> shift){
        return shift.get(0);
    }
}
class Test{
    public <B,N,M> B getArrayList(ArrayList<B> shift){
        return shift.get(0);
    }
}

上述这两种写法都是没有问题的。泛型方法的泛型类是在方法被调用的时候被指定的。

3:静态泛型方法

下面是之前我一直不会的泛型方法和静态修饰符连用:

class Test{
    public static final <B,N,M>  B getArrayList(ArrayList<B> shift){
        return shift.get(0);
    }

    public static void main(String[] args) {
//        ArrayList<Object> list = new ArrayList();
//        Object arrayList = getArrayList(list);
        ArrayList<String> list = new ArrayList();
        String arrayList = getArrayList(list);
    }
}

这里我们入参当中反省是Object方法执行后返参就是Object,String的话返参就是String

4:泛型方法与可变参数

class Shit{
    public static final <B>  B getArrayList(B...b){
        for (B b1 : b) {
            System.out.println(b);
        }
        return null;
    }

    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList();
        String arrayList = getArrayList("a","b","c");
        Integer shit = getArrayList(1,2,3);
    }
}

总结:
1:泛型方法可以独立于泛型类使用泛型的能力
2:如果static方法需要使用泛型鞥哪里,就必须成为泛型方法,不能简单使用泛型类的泛型变变量。

第二章:泛型通配符

一:什么是类型通配符

类型通配符就是一个?代替具体的类型实参,类型通配符是类型实参,而不是类型形参。

二:类型通配符上限

class Box<E>{
    private E first;
    public E getFirst(){
        return this.first;
    }
    public void setFirst(E first){
        this.first = first;
    }
}

class Test {
    public static void main(String[] args) {
        Box<Integer> box = new Box<>();
        showBox(box);
    }

    public static void showBox(Box<? extends Number> box){
        Number first = box.getFirst();
        System.out.println(first);
    }
}

类/接口 <? extends 实参类型> 要求该泛型的类型,只能是是参类型或者是实参类型的子类类型。

这里要注意的是,类型通配符上限的这样的list是不能填充元素的。因为我们采用的是上线通配符。不知道里边的类型有多下限,所以限制填充元素。

三:类型通配符下限

类/接口<? super 实参类型> 要求该泛型的类型,只能是实参类型或者是实参类型的父类类型。

类型通配符下限这种方式,可以填充元素,但是不能保证元素的类型的 约束要求,因为可以把实参类型的子类给添加进去。

集合遍历的时候,都会使用Object来接收。

第三章:类型擦除

泛型是Java 1.5版本才引进的概念,在这之前是没有泛型的,但是,泛型代码能够很好地和之前版本的代码兼容。那是因为,泛型信息只存在于代码编译阶段,在进入JVM之前,与泛型相关的信息会被擦除掉,我们称之为–类型擦除

Java中的泛型信息,只存在于编译阶段,进入到JVM之后就没有所谓的泛型类型了。

一:无限制类型擦除

在这里插入图片描述
泛型标识是T,生成字节码文件过程中T都用Object替换了。我们通过反射拿对应成员变量的类型的时候,就变成了Object。

二:有限制类型擦除

在这里插入图片描述
有上限类型,就把T转换成了上限类型,我们通过反射拿对应成员变量的类型的时候,就变成了Number

三:擦除方法中类型参数

在这里插入图片描述
这里T变Number,反射去拿就行。

四:桥接方法

在这里插入图片描述
生成了一个桥接方法,来保证实现关系。

package com.dashu.nettytest;

import java.lang.reflect.Method;

public interface Apple <T>{

    T info (T t);
}

class infoImpl implements Apple<Integer>{

    @Override
    public Integer info(Integer integer) {
        return integer;
    }

}

class Test999{
    public static void main(String[] args) {
        Class<infoImpl> infoClass = infoImpl.class;
        Method[] declaredMethods = infoClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod.getName()+":"+declaredMethod.getReturnType().getSimpleName());
        }
    }
    //info:Integer
    //info:Object
    //
    //Process finished with exit code 0
}

有两个info方法,保证泛型,还有一个桥接方法。字节码反编译之后,没有这个了。

第四章:泛型与数组

泛型数组的创建
可以声明带泛型的数组的引用,但是不能直接创建带泛型的数组的对象。
可以通过java.lang.relect.Array的newInstance(Class,int)来创建T[]数组

一:带泛型数组的对象不可以直接创建

在这里插入图片描述
如图所示,直接创建的
话是无法编译过去的。我们可以声明左边这样的引用。
非泛型的可以直接创建这是没有任何问题的。

class Test999{
    public static void main(String[] args) {
        ArrayList[] list = new ArrayList[5];
        ArrayList<String>[] listArr = list;

        ArrayList<Integer> intList = new ArrayList<>();
        intList.add(100);

        list[0] = intList;
        String s = listArr[0].get(0);
        System.out.println(s);
        
        //Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
        //	at com.dashu.nettytest.Test999.main(Apple.java:28)
        //
        //Process finished with exit code 1

    }
}

这种方式使用数组是不安全的。我们声明的是泛型的ArrayList数组,指向了一个原生类型的ArrayList的数组对象,原生类型的话,可以添加任何类型的元素,导致后续类型转换报错。我们应该这么写:

class Test999{
    public static void main(String[] args) {
//        ArrayList[] list = new ArrayList[5];
//        ArrayList<String>[] listArr = list;
        ArrayList<String>[] listArr = new ArrayList[5];

        ArrayList<String> stringList = new ArrayList<>();
        stringList.add("abc");

//        list[0] = intList;
        listArr[0] = stringList;
        String s = listArr[0].get(0);
        System.out.println(s);
        //abc
    }
}

我们使用泛型数组的时候,我们都是拿着泛型类型去创建一个这样的引用,而创建的对象呢不能创建泛型的数据类型,而是创建原生的数据类型赋值给引用。操作的时候,我们拿着引用去操作即可。

泛型在编译器会进行泛型擦除,而数组会在整个编译器期间持有他的数据类型,他们在设计上就是有冲突的,所以不能直接创建带有泛型类型的数组对象。

我们可以通过反射的方式创建一个泛型的数组。

二:通过反射创建泛型的数组

在这里插入图片描述
这个是不允许的,类型都没搞清楚,就直接new对象,肯定不行。
应该这么来写:

class Test999{
    public static void main(String[] args) {
        Fruit<String> fruit = new Fruit<>(String.class,3);
        fruit.put(0,"aaa");
        fruit.put(1,"bbb");
        fruit.put(2,"ccc");

        System.out.println("Arrays.toString(fruit.getArray()) = " + Arrays.toString(fruit.getArray()));
    }
    //Arrays.toString(fruit.getArray()) = [aaa, bbb, ccc]
}
class Fruit<T> {
    private T[] array;

    public Fruit(Class<T> clz, int length){
        array =(T[]) Array.newInstance(clz,length);
    }

    public void put(int index,T t){
        array[index] = t;
    }
    
    public T get(int index){
        return array[index];
    }
    
    public T[] getArray(){
        return array;
    }

}

最好不用泛型数组,要用泛型集合代替。

第五章:泛型与反射

一:反射中常用的泛型类

Class
Constructor

二:泛型对反射的好处

class Test999{
    public static void main(String[] args) throws Exception {
        Class<Person> personClass = Person.class;
        Constructor<Person> constructor = personClass.getConstructor();
        Person person = constructor.newInstance();
    }
}

class Person {
    private String name;

    public String getName(){
        return name;
    }

    public void setName(String name){
        this.name = name;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/97145.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SQL进阶笔记

SQL进阶笔记 CASE表达式 简单case表达式 case sexwhen 1 then 男when 2 then 女else 其他 end搜索case表达式 case when sex 1 then 男when sex 2 then 女else 其他 end编写SQL语句的时候需要注意&#xff0c;在发现为真的WHEN子句时&#xff0c;CASE表达式的真假值判断就会中…

Java并发编程(一)—— FutureTask超详细教程

一、前言 创建线程有几种方式?这个问题的答案应该是可以脱口而出的吧: 继承 Thread 类实现 Runnable 接口但这两种方式创建的线程是属于三无产品: 没有参数没有返回值没办法抛出异常用着三无产品总是有一些弊端,其中没办法拿到返回值是最让人不能忍的,于是 Callable 就诞…

TeamsApp LukcyDraw升级之路 之 DB Infra 篇

今天继续 LuckyDraw 的升级之路&#xff0c;之前在1500以上用户同时使用的时候&#xff0c;特别是在短时间内大家一起点击参与抽奖参的时候&#xff0c;服务会出现大量的错误&#xff0c;分析后发现&#xff0c;出现错误的原因基本都是 Azure Storage Table 返回的。当时使用 A…

【GRU回归预测】基于matlab卷积神经网络结合门控循环单元CNN-GRU数据预测(多输入单输出)【含Matlab期源码 2274期】

⛄一、CNN-GRU数据预测 1 理论基础 1.1 CNN算法 负荷序列数据为一维数据&#xff0c;用一维卷积核对数据进行卷积处理&#xff0c;以获取数据的特征。 现设定卷积核的维度为3&#xff0c;移动步长为1&#xff0c;对输入数据进行卷积&#xff0c;以获得特征图图谱&#xff0c;即…

记录那不用百度的美好代码

努力工作&#xff0c;日益消瘦&#xff0c;总有些代码不想记住。我称之为&#xff1a;拿来即用系列 一&#xff1a;格式化时间 1.去除时间的T // 去除日期中T function timeFormatSeconds(time) {if(!time) return time;var date time.substr(0, 10); //年月日var hours ti…

jsp+ssm计算机毕业设计宠物商城【附源码】

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; JSPSSM mybatis Maven等等组成&#xff0c;B/S模式 Mave…

基于SPRINGBOOT的健康饮食管理系统

11-8开发工具(eclipse/idea/vscode等)&#xff1a; 数据库(sqlite/mysql/sqlserver等)&#xff1a; 功能模块(请用文字描述&#xff0c;至少200字)&#xff1a; 基于springboot框架&#xff0c;通过对事物健康程度的总结与归纳&#xff0c;进行软件设计&#xff0c;并通过优秀的…

go 语言Http服务器远程执行shell命令

背景 想要在服务器上起一个http服务器&#xff0c;用来远程执行shell命令并且返回。 基础版 package mainimport ("io""log""net/http" )func main() {// Hello world, the web serverhelloHandler : func(w http.ResponseWriter, req *http.R…

性能脚本:用案例和图示帮你理解HTTP协议

当前使用得最为广泛的应用层协议就是 HTTP 了。我想了好久,还是觉得应该把 HTTP 协议写一下。 因为做性能测试分析的人来说,HTTP 协议可能是绕不过去的一个槛。在讲 HTTP 之前,我们得先知道一些基本的信息。 HTTP(HyperText Transfer Protocol,超文本传输协议),显然是…

linux shell编程

linux shell编程1.shell脚本概述2.脚本的调用形式3.shell脚本语法3.1 脚本开头标识3.2 脚本注释3.3 给脚本加上可执行权限3.4 shell脚本的运行方式3.5 检测脚本是否正确3.6 重定向的使用3.7 获取上一条命令的执行结果4.变量4.1 变量的定义和使用4.2 从键盘上读取变量&#xff1…

(十七)Vue之自定义指令

文章目录自定义指令局部指令回调函数形式配置对象形式全局配置回调函数形式配置对象形式Vue学习目录 上一篇&#xff1a;&#xff08;十六&#xff09;Vue之内置指令 先看两个需求&#xff1a; 需求1&#xff1a;定义一个v-big指令&#xff0c;和v-text功能类似&#xff0c;…

陈表达学VBA-msgbox辨别用户选择和代码调用

今天有个老客户提出需求&#xff1a; 模块&#xff1a;sub 过程A( ) ①如果是单独使用时则显示msgbox选择消息框 ②如果A过程调用B过程&#xff0c;则不显示消息框&#xff0c;默认选择是 msgbox没有设置默认值这个功能&#xff0c;msgbox也没有参数可以失败是用户点击还是代…

pytorch基础操作(六)利用L2范数(权重衰减)解决线性模型过拟合问题

一、一些概念 训练误差&#xff08;training error&#xff09; 是指&#xff0c;模型在训练数据集上计算得到的误差。 泛化误差&#xff08;generalization error&#xff09; 是指&#xff0c;模型应⽤在同样从原始样本的分布中抽取的⽆限多数据样本时&#xff0c;模型误差的…

【Linux】Linux基本指令

Linux基本指令1.ls指令2.pwd指令3.cd指令3.touch 指令4.mkdir指令5. rmdir指令 && rm 指令5.1 rmdir5.2 rm6.tree 指令7.man指令8.cp指令9.mv指令10. cat指令11.echo指令12.more指令13.less指令14.wc指令15.head指令16.tail指令17. | &#xff08;管道指令&#xff09;…

每天五分钟机器学习:PCA算法如何确定数据压缩降维的最佳维度?

本文重点 上节课程中我们已经学习了pca算法,已经知道了如何将n维特征变量降到k维,k是PCA算法的一个参数,也被称为主成分的数量。那么现在就产生了一个问题,这个问题就是如何选择K,因为PCA要做的就是要尽量减少投射的平均均方误差,所以K的选择很关键。 平均均方误差 其中…

22.MongoDB删除操作效率及相关问题验证

最近遇到一个了一个MongoDB数据删除的问题&#xff0c;需要一次性删除上线即1.5年前~1年前的数据且之后每天清空一年过期的数据。在数据量比较大的情况下何种方式的删除效率最高是一个值得研究的问题&#xff0c;本文通过实际测试找出其中规律。 本文采用腾讯云mongodb集群进行…

基于java的连连看游戏设计-计算机毕业设计

项目介绍 基于java设计的连连看游戏规则是模仿网络上的最普通的连连看游戏&#xff0c;主要是鼠标点击两次的图片能否消去的问题。当前&#xff0c;前提是点击两张相同的图片&#xff0c;若是点击的是同一张图片或者两张不同的图片&#xff0c;则不予处理。在两张相同的图片所…

这份pdf成功让我拿下了蚂蚁金服、字节跳动、小米等大厂的offer

关于程序员&#xff0c;除了做项目来提高自身的技术之外&#xff0c;还有一种提升自己的专业技能就是&#xff1a;多&#xff01;看&#xff01;书&#xff01; 小编整理出一篇Java进阶架构师之路的核心知识&#xff0c;同时也是面试时面试官必问的知识点&#xff0c;篇章也是…

点云可视化工具

点云可视化工具 平时查看点云文件主要是用CloudCompare&#xff0c;基本上也就是打开看看这个点云大概是个什么样子&#xff0c;很少会在CloudCompare对点云进行处理&#xff0c;它可以直接将点云拖进软件进行显示也还是挺方便的。但是还是有点点不是很方便的地方&#xff0c;…

Linux从入门到进阶学习(Ⅲ):Linux权限管控

目录 1 root用户 1.1 su 1.2 sudo 2 用户和用户组 3 查看权限控制信息 4 修改权限控制 4.1 chmod命令 4.2 chown命令 1 root用户 1.1 su root用户即超级管理员 su [-] [用户名] 切换到root用户&#xff0c;exit退回普通用户从 -&#xff1a;可选&#xff0…