Java泛型详解(进阶四)

2023-10-31

友情提醒

先看文章目录,大致了解知识点结构,直接点击文章目录可以跳转到文章指定位置。

第一章、泛型介绍

1.1)什么是泛型

百度百科:Java泛型是J2 SE1.5中引入的一个新特性,其本质是参数化类型(通过泛型指定的类型来控制形参具体的类型),操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

①泛型的格式: <具体的数据类型>
② 集合加泛型以后,集合中只能保存具体的某一种数据类型。

ArrayList<String> list1 = new ArrayList<>();

③泛型只支持引用数据类型,不支持基本数据类型。

//报错
// ArrayList<int> list2 = new ArrayList<int>();

④等号左右两侧<>内的泛型实参类型不支持多态性,只能是相同类型

//报错
// List<Object> list3 = new ArrayList<Integer>();

1.2)泛型为什么出现

①集合中是可以保存任意类型的对象。这些对象一旦保存到集合中之后,都会被提升成Object类型,通过对类型Object的引用来实现参数的“任意化”。
②“任意化”带来的缺点是取出数据时要显示的强制向下类型转换,可是取数据时我们并不知道是什么类型,使用保存的对象的特有方法或者属性时,需要向下转型并且向下转型还有风险,还得使用 instanceof关键字进行判断。

ArrayList list = new ArrayList();
        list.add("Java");
        list.add("python");
        list.add("mysql");

//如下集合中添加其他引用数据类型,但是向下转型时我们不知道是什么类型数据,容易出错
//运行时才提示ClassCastException
//   list.add(new Object());
        
        for (Object o : list) {
//由于集合可以存储任意类型的对象,而这里只是适合String类型的强制类型转换,
//加入其他数据类型运行时会报classCastException类转换
            String str = (String) o;
            System.out.print(str.length()+"  ");
        }

总之很麻烦。于是出现了泛型解决这个问题

ArrayList<String> list = new ArrayList<>();
        list.add("Java");
        list.add("python");
        list.add("mysql");
        list.add("html");
        list.add("javascript");
//加入泛型,变量的类型被限制,若加入其他引用类型编译阶段提示错误,add String in ArrayList cannot be applied to Object
//而不是运行时提示ClassCastException
 // list.add(new Object());	
 
        for (String s : list) {
            System.out.println(s.length());
        }

泛型的好处:
①解决了集合中存储数据的不安全性;
②避免了运行时的异常,把运行时可能发生的异常,放在编译时作为编译错误处理了;
③省略了代码中的强制类型转换的书写;

第二章、泛型类/接口/方法

2.1)为什么要用泛型类/接口

①在类名或接口名后面使用<标识符>,在使用的时候,可以指定其中的类型。重点:使用的时候,指定其中的类型
②注意:不可以定义泛型数组
比如典型的容器类:在ArrayList类上有一个泛型的参数:E
在这里插入图片描述
当我们使用ArrayList的时候,我们要存储String类型就可以进行指定:

//指定ArrayList集合存储String类型
ArrayList<String> list = new ArrayList<>();

2.2)自定义泛型类

①在类名或接口名后面使用<标识符>,标识符可以是任意的字母、数字、下划线和 $ 。但是这里一般规范使用单个大写字母。
②泛型类不可以继承Exception类,即泛型类不可以作为异常被抛出。
③不可以用泛型构造对象,即:first = new T();是错误的
例子一:创建对象的时候,可以指定成员变量类型

//自定义泛型类:
class Demo<T>{
//num这个成员变量的类型为T,类型由外部指定 
    T num;

    public void func(T num){
       System.out.println(num);
    }
}
// ----------------------------分割----------------------------------
public class TestGeneric_2 {
    public static void main(String[] args) {
        //创建Demo对象的过程中,明确泛型形参的具体类型 --> 定义泛型实参类型
        //Demo<T> d = new Demo<>(); T被指定为string了
        Demo<String> d = new Demo<>();
        d.func("abc");//abc
        
		//Demo<T> d = new Demo<>(); T被指定为Double了
        Demo<Double> d2 = new Demo<>();
        d2.func(123.23);//123.23

    }
}

例子二:创建对象的时候,可以指定成员变量类型

//通过泛型,每次创建Student对象都可以指定成员变量score 的类型
class Student<E>{
//score这个成员变量的类型为E,类型由外部指定 
     E score;
    public Student( E score) {//泛型构造方法形参的类型也为E,E的类型由外部指定
        this.score = score;
    }
    public Student() { }
    @Override
    public String toString() {
        return "Student{" + " score=" + score + '}';
    }
}
// ----------------------------分割----------------------------------
public class TestGeneric_3 {
    public static void main(String[] args) {
    //通过泛型,每次创建Student对象都通过构造方法指定成员变量score 的类型
        Student<String> s1 = new Student<>( "优秀");
    //必须是类类型,不能是基本类型
        Student<Integer> s2 = new Student<>( 88);
        Student<Character> s3 = new Student<>('B');
        System.out.println(s1);//Student{ score=优秀}
        System.out.println(s2);//Student{ score=88}
        System.out.println(s3);//Student{ score=B}
    }
}

2.3)多泛型变量

ps:
在原来的T后面用逗号隔开,写上其它的大写字母

//在原来的T后面用逗号隔开,写上其它的大写字母
public class More<T,U> {
    private  T x;
    private T y;
    private U name;
    
    public T getX() {   return x;    }
    public void setX(T x) {  this.x = x; }
    public T getY() {   return y;  }
    public void setY(T y) {  this.y = y; }
    public U getName() { return name;  }
    public void setName(U name) {  this.name = name;    }
    public More() {    }
    public More(T x, T y, U name) {
        this.x = x;
        this.y = y;
        this.name = name;
    }

public static void main(String[] args) {
        //使用
More<Integer,String> morePoint = new More<Integer, String>(12,23,"尔康");
System.out.println(morePoint.getX()+morePoint.getY()+morePoint.getName());//35尔康
    }
}

2.4)自定义泛型接口

①在类名或接口名后面使用<标识符>,标识符可以是任意的字母、数字、下划线和 $ 。但是这里一般规范使用单个大写字母。

//自定义泛型接口
interface Inter<T>{
    public abstract T show(T t);
}
// ----------------------------分割----------------------------------
//类实现接口的过程中明确泛型形参的具体类型
class InterImpl implements Inter<String>{
    @Override
    public String show(String s) {
    System.out.println(s);
        return s;
    }
}
// ----------------------------分割----------------------------------
//类实现接口的过程中没有明确泛型形参的具体类型
class InterImpl2<E> implements Inter<E> {
    @Override
    public E show(E e) {
    System.out.println(e);
        return e;
    }
}
// ----------------------------分割----------------------------------
public class TestGeneric_5 {
    public static void main(String[] args) {
        //在实例化对象的过程中明确泛型形参的具体类型
        InterImpl2<Integer> ii = new InterImpl2<>();
        ii.show(123);//123

        //在实例化对象的过程中去除了<>的定义 ==> 称为:泛型的擦除操作
        //理解:所有的泛型形参类型位置都同步为Object类型
        InterImpl2 ii2 = new InterImpl2();
        ii2.show("abc");//abc
    }
}

2.5)自定义泛型方法

①有时方法需要接收的数据类型和类上外界指定的类型不一致。这时我们可以在这个类中的这个方法上单独给这个方法设定泛型。
②静态方法中不能使用类的泛型,因为类的泛型是在实例化之后才创建,而静态方法是在实例化之前就被创建了的。一个存在的东西不能调用一个不存在的东西。

class Demo2<A>{
    //静态方法中不能使用类的泛型,因为类的泛型是在实例化之后才创建,而静态方法是在实
    // 例化之前就被创建了的。一个存在的东西不能调用一个不存在的东西。
   // public static void func(A a) {     System.out.println(a); }//这会报错噢
    public  void func(A a) {     System.out.println(a); } //这不会

}

③泛型方法,可以声明为静态的。原因:泛型参数是在调用的时候确定的,并非是在实例化类的时候确定。

class Demo2<A>{
    public  void func(A a) {     System.out.println(a); }
    public static <B> void method(B b) {
        System.out.println(b);
    }
}

//------------------------分割------------------------------
public class TestGeneric_4 {
    public static void main(String[] args) {
        //实例化对象的过程中,就明确了泛型形参A的具体类型,但是B类型仍然不明确
        Demo2<String> d = new Demo2<>();
        d.func("hello");
        //在调用method方法传入实参数据的一刹那,B类型的具体类型就明确了
        d.method(123);
	}
}

④泛型方法只是针对于方法本身来讲,与方法所在的类或接口是否为泛型类、泛型接口没有关联。

public class TestGeneric_6 {
    public static void main(String[] args) {
        method(123);
    }
    public static  <B> void method(B b) {
        System.out.println(b);
    }
}

2.6)泛型方法的应用

    //自定义泛型方法     遍历不同类型的数组元素
    public static <T> void printArray(T[] arr) {    
        for (T t : arr) {
            System.out.print(t + " ");
        }
        System.out.println();
    }
//------------------------分割-----------------------    
 public static void main(String[] args) {
        String[] arr = {"aaa", "bbb", "ccc", "ddd"};
        Integer[] arr2 = {123, 456, 789};

        printArray(arr);
        printArray(arr2);
    }

第三章、泛型通配符和泛型限定

3.1)泛型的通配符

①? 表示泛型的通配符,表示集合中的任意数据类型,传递过来的表示什么数据类型,?就表示什么数据类型
②现在需要定义一个方法既可以接收ArrayList,又可以接收HashSet集合容器对象,只能使用它们的父类或接口类型来接收,这样就可以使用Collection接口类型接收。但是使用泛型不知道具体的数据类型,可以使用泛型的通配符 ? 来表示。

    //自定义方法:遍历打印Collection集合元素
    //这里书写Object不可以,因为泛型格式要求两侧必须类型一致,所以使用?通配符
    //Collection<Object> coll=new ArrayList<String>()
    public static void printCollection(Collection<?> coll) {
        for (Object o : coll) {
            System.out.print(o + " ");
        }
        System.out.println();
    }
    //------------------------分割-------------------------------
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("abc");
        list.add("def");
        HashSet<Integer> set = new HashSet<>();
        set.add(123);
        set.add(456);
        printCollection(list); //参数位置:Collection<String> coll = new ArrayList<String>()
        printCollection(set); //参数位置:Collection<Integer> coll = new HashSet<Integer>()
    }
}

3.2)泛型的限定

①上限通配符:<? extends T>,泛型中的参数必须是 T 或者 T 的子类
②下限通配符:<? super T>,泛型中的参数必须是 T 或者 T 的超类
③无限定通配符:<?>,类型参数可以是任何类型

public class TestGeneric_2 {
    public static void main(String[] args) {
        //创建Person和其子类型的集合容器
        ArrayList<Person> pers = new ArrayList<>();
        ArrayList<Student> stus = new ArrayList<>();
        HashSet<Teacher> teas = new HashSet<>();

        //创建Animal和其子类型的集合容器
        ArrayList<Animal> ans = new ArrayList<>();
        ArrayList<Dog> dogs = new ArrayList<>();
        HashSet<Cat> cats = new HashSet<>();

        //遍历集合元素
        printCollection(pers);
        printCollection(stus);
        printCollection(teas);

//        printCollection(ans);//报错
//        printCollection(dogs);//报错
//        printCollection(cats);//报错
    }
//printCollection接受的参数必须是Person以及它的子类
    public static void printCollection(Collection<? extends Person> coll) {
        for (Person ps : coll) {
            System.out.print(ps + " ");
        }
        System.out.println();
    }
}

class Person{}
class Student extends Person{}
class Teacher extends Person{}

class Animal{}
class Dog extends Animal{}
class Cat extends Animal{}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java泛型详解(进阶四) 的相关文章

  • 如何使用 Apache POI API 将图像添加到 pptx 中添加的图像占位符?

    我已经预定义了带有文本和图像占位符的 pptx 模板 我如何从模板访问和修改这些占位符 我可以使用 POI pptx API 直接将图像和文本添加到幻灯片中 但如何将其添加到模板的占位符中 请参阅链接以了解如何添加占位符来创建固定模板 ht
  • Java中字符串中特殊字符的替换

    Java中如何替换字符串 E g String a adf sdf 如何替换和避免特殊字符 您可以删除除此之外的所有字符可打印的 ASCII 范围 http en wikipedia org wiki ASCII ASCII printab
  • Eclipse 在源代码管理中保存操作

    我们希望找到一种在签入之前执行代码标准的 轻量级 方法 我们真的很喜欢使用 Eclipse 内置的想法保存操作 go to Preferences gt gt Java gt gt Editor gt gt Save Actions 其中有
  • 使用 JPA Criteria API 进行分页的总行数

    我正在系统中为实体实现 高级搜索 功能 以便用户可以使用该实体的属性上的多个条件 eq ne gt lt 等 来搜索该实体 我正在使用 JPA 的 Criteria API 动态生成 Criteria 查询 然后使用setFirstResu
  • 这个函数(for循环)空间复杂度是O(1)还是O(n)?

    public void check 10 for string i list Integer a hashtable get i if a gt 10 hashtable remove i 这是 O 1 还是 O n 我猜测 O n 但不是
  • org.apache.sling.api.resource,version=[2.3,3) -- 无法解析

    您好 我无法访问我的项目内容 我已经上传了从 CQ 访问内容所需的所有包 我唯一能看到的是 org apache sling api resource version 2 3 3 无法解析 这是否是异常的原因 如果是 请告诉我如何解决 中Q
  • 如何使用 Java 处理 Selenium WebDriver 中的新窗口?

    这是我的代码 driver findElement By id ImageButton5 click Thread sleep 3000 String winHandleBefore driver getWindowHandle drive
  • Java AES 128 加密方式与 openssl 不同

    我们遇到了一种奇怪的情况 即我们在 Java 中使用的加密方法会向 openssl 生成不同的输出 尽管它们在配置上看起来相同 使用相同的键和 IV 文本 敏捷的棕色狐狸跳过了懒狗 加密为 Base64 字符串 openssl A8cMRI
  • java中如何连接字符串

    这是我的字符串连接代码 StringSecret java public class StringSecret public static void main String args String s new String abc s co
  • 我需要什么库才能在 Java 中访问这个 com.sun.image.codec.jpeg?

    我正在用java创建一个图像水印程序 并导入了以下内容 import com sun image codec jpeg JPEGCodec import com sun image codec jpeg JPEGEncodeParam im
  • Java 文件上传速度非常慢

    我构建了一个小型服务 它从 Android 设备接收图像并将其保存到 Amazon S3 存储桶中 代码非常简单 但是速度非常慢 事情是这样的 public synchronized static Response postCommentP
  • 在 S3 中迭代对象时出现“ConnectionPoolTimeoutException”

    我已经使用 aws java API 一段时间了 没有遇到太多问题 目前我使用的是库 1 5 2 版本 当我使用以下代码迭代文件夹内的对象时 AmazonS3 s3 new AmazonS3Client new PropertiesCred
  • 画透明圆,外面填充

    我有一个地图视图 我想在其上画一个圆圈以聚焦于给定区域 但我希望圆圈倒转 也就是说 圆的内部不是被填充 而是透明的 其他所有部分都被填充 请参阅这张图片了解我的意思 http i imgur com zxIMZ png 上半部分显示了我可以
  • 很好地处理数据库约束错误

    再一次 它应该很简单 我的任务是在我们的应用程序的域对象中放置一个具有唯一约束的特定字段 这本身并不是一个很大的挑战 我刚刚做了以下事情 public class Location more fields Column unique tru
  • 在游戏视图下添加 admob

    我一直试图将 admob 放在我的游戏视图下 这是我的代码 public class HoodStarGame extends AndroidApplication Override public void onCreate Bundle
  • 欧洲中部时间 14 日 3 月 30 日星期五 00:00:00 至 日/月/年

    我尝试解析格式日期Fri Mar 30 00 00 00 CET 14至 日 月 年 这是我的代码 SimpleDateFormat formatter new SimpleDateFormat dd MM yyyy System out
  • Struts 2 + Sitemesh 3 集成 - FreemarkerDecoratorServlet 中的 NPE

    我将 Struts 2 版本 2 3 14 3 与 Sitemesh 3 版本 3 0 alpha 2 一起使用 并且在某些情况下遇到 NullPointerException 首先 这是我的 web xml 中的 struts2 site
  • HQL Hibernate 内连接

    我怎样才能在 Hibernate 中编写这个 SQL 查询 我想使用 Hibernate 来创建查询 而不是创建数据库 SELECT FROM Employee e INNER JOIN Team t ON e Id team t Id t
  • 为什么C++代码执行速度比java慢?

    我最近用 Java 编写了一个计算密集型算法 然后将其翻译为 C 令我惊讶的是 C 的执行速度要慢得多 我现在已经编写了一个更短的 Java 测试程序和一个相应的 C 程序 见下文 我的原始代码具有大量数组访问功能 测试代码也是如此 C 的
  • Trie 数据结构 - Java [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 是否有任何库或文档 链接提供了在 java 中实现 Trie 数据结构的更多信息 任何帮助都会很棒 Thanks 你可以阅读Java特里树

随机推荐

  • Dell G3安装外星人控制中心

    Dell的外星人电脑和G系列的电脑 都支持安装外星人控制中心 Dell 支持 在驱动程序中下载Alienware OC Controls Application和Alienware Command Center Application 我们
  • 分布式事务解决方案和代码落地

    在学习Spring cloud alibaba Seata之前 我们先来了解一下分布式事务的常用解决方案和代码实现 看完出去面试再遇到分布式事务的问题 战无不胜 文章包括了2PC 3PC刚性事务 TCC 本地消息表 可靠性消息 双写对账 最
  • ICML 2018

    感谢阅读腾讯AI Lab微信号第34篇文章 当地时间 7 月 10 15 日 第 35 届国际机器学习会议 ICML 2018 在瑞典斯德哥尔摩成功举办 ICML 2018 所接收的论文的研究主题非常多样 涵盖深度学习模型 架构 理论 强化
  • nacos服务对微服务客户端心跳检测原理

    微服务注册到nacos中的代码 com alibaba nacos client naming net NamingProxy 的registerService String serviceName String groupName Ins
  • 程序设计笔记

    程序设计笔记 基础知识2020 12 7 程序设计实验须知 参考书目 计算机解决问题的步骤 代码结构 程序运行过程 基础知识 程序设计实验须知 实验前必须做好实验预习 参考书目 谭浩强C语言系列 1 C语言程序设计教程 第3版 2 C语言题
  • C#调用C++dll,C++中void*与C#类型的对应关系

    IntPtr input Marshal AllocHGlobal 100 IntPtr output Marshal AllocHGlobal 100 C 对应的int misposTrans void input void output
  • Spring Boot application.properties或application.yml相关配置

    COMMON SPRING BOOT PROPERTIES This sample file is provided as a guideline Do NOT copy it in its entirety to your own app
  • 饿了吗开源组件库Element模拟购物车系统

    传统的用html jquery来实现购物车系统要非常的复杂 但是购物车系统完全是一个数据驱动的系统 因此采用诸如Vue js angular js这些框架要简单的多 饿了吗开源的组件库Element是基于Vue js 2 0实现的 该组件库
  • mac adt识别 手机

    1 中断输入 system profiler SPUSBDataType system profiler SPUSBDataType 2 找到 Vendor ID Vendor ID 0x2a45 3 echo 0x2a45 gt andr
  • STM32定时器输出比较中断

    在使用定时器输出比较中断来输出PWM信号时 需要将定时器的输出比较匹配模式设置为 非预装载模式 或 预装载模式 预装载模式是指在定时器计数器计数到比较寄存器中的值时 会将下一个要比较的值预先装入到计数器中 而非立即加载 这种模式可以防止由于
  • 如何在Windows下使用OpenGL 2.0的API(包括GLSL)http://sourceforge.net/projects/mesa3d/files/MesaLib/7.0.3/MesaLi

    转 如何在Windows下使用OpenGL 2 0的API 包括GLSL http pkgs fedoraproject org repo pkgs tigervnc MesaLib 7 6 1 tar bz2 7db4617e9e10ad
  • 改变this指向

    改变this指向 题目 封装函数 f 使 f 的 this 指向指定的对象 改变this指向有三种方式 call bind apply call apply 只有一个区别 就是 call 方法接受的是若干个参数的列表 而 apply 方法接
  • 645. 错误的集合

    集合 s 包含从 1 到 n 的整数 不幸的是 因为数据错误 导致集合里面某一个数字复制了成了集合里面的另外一个数字的值 导致集合 丢失了一个数字 并且 有一个数字重复 给定一个数组 nums 代表了集合 S 发生错误后的结果 请你找出重复
  • keil修改字体、文本颜色、背景颜色,global.prop使用

    keil 空乏的文本颜色和背景严重影响使用感 下面教大家如何修改仿VS的黑色主题 自定义字体颜色和背景 点击 Configuration 小扳手图标 Colors Fonts 主要修改 All Editors 和 C C Editor fi
  • 用命令行打开指定目录

    目录 目的 基本指令 打开win格式的路径 写成脚本 打开服务器地址 用简单的命令执行脚本 目的 用命令行打开指定目录 基本指令 nautilus 路径命令可以在ubuntu上直接打开此路径的目录 如nautilus workspace 打
  • 【python实现基于深度学习的文本情感分类(1)】——要做什么?

    All our works can be found on the github project 俗话说得好 万事开头难 往后中间难 最后结尾难 为什么做这个 这是博主人工智能基础课的大作业 内容是用深度学习做金融文本的情感分类 语言 工具
  • ES聚合统计group by,sum,max,min,avg,count等聚合统计

    基本查询 通过match实现全文搜索 FIELD 就是我们需要匹配的字段名 TEXT 就是我们需要匹配的内容 query match FIELD TEXT 通过term实现精确搜索 FIELD 就是我们需要匹配的字段名 TEXT 就是我们需
  • java中Arrays.sort()

    该方法是Arrays类的静态方法 默认对数组进行从小到大进行排序 1 Arrays sort int a 这种形式是对一个数组的所有元素进行排序 并且是按从小到大的顺序 import java util public class Test
  • python中列表和字典的常用知识点

    本文主要介绍python列表和字典的常用知识点 增 删 改 查 一 列表 1 1 提取元素 两种取法 一个是取单个值 一个是取切片 不同点 取值直接使用 列表名 索引值 取到的值的数据类型是元素的数据类型 切片的格式 列表名 左索引值 右索
  • Java泛型详解(进阶四)

    目录 友情提醒 第一章 泛型介绍 1 1 什么是泛型 1 2 泛型为什么出现 第二章 泛型类 接口 方法 2 1 为什么要用泛型类 接口 2 2 自定义泛型类 2 3 多泛型变量 2 4 自定义泛型接口 2 5 自定义泛型方法 2 6 泛型