Java泛型集合简明教程

news2024/11/24 20:15:01

前言

我们编写一个数组并对数组进行排序,不管是对浮点型数组、整型数组、字符串数组或者是其他任何类型的数组进行排序,我们可以利用方法重载的方式,针对每种类型的数组分别编写一个排序方法,需要为几种类型的数组排序,我们就定义几个排序方法。

如果是这么实现的,代码可重用性太差。

或者是定义一个方法,里面设置一个Object[]类型的参数,这样无论是哪种类型都可以处理了。这样定义方法我们需要在Object类型和整型、String类型或其他类型之间进行强制类型转换。所以这样做就无法保证集合中元素的类型安全,稍一不慎就可能会导致 ClassCastException类型转换异常。

而且集合有个缺点,就是把一个对象放入集合里之后,集合就会遗失这个对象的数据类型,当再次取出该对象时,该对象的类型就变成了 Object 类型(其运行时类型没变)。

所以为了解决上述问题,从 Java 1.5 开始提供了泛型。泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型,并且所有的强制转换都是自动和隐式的,提高了代码的重用率。

泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。

泛型方法

你可以写一个泛型方法,该方法在调用时可以接收不同类型的参数。根据传递给泛型方法的参数类型,编译器适当地处理每一个方法调用。

下面是定义泛型方法的规则:

  • 所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子中的 <E>)。
  • 每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
  • 类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
  • 泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像 int、double、char 等)。

java 中泛型标记符:

  • E - Element (在集合中使用,因为集合中存放的是元素)
  • T - Type(Java 类)
  • K - Key(键)
  • V - Value(值)
  • N - Number(数值类型)
  • - 表示不确定的 java 类型

实例

下面的例子演示了如何使用泛型方法打印不同类型的数组元素:

public class GenericMethodTest
{
   // 泛型方法 printArray                         
   public static < E > void printArray( E[] inputArray )
   {
      // 输出数组元素            
         for ( E element : inputArray ){        
            System.out.printf( "%s ", element );
         }
         System.out.println();
    }
 
    public static void main( String args[] )
    {
        // 创建不同类型数组: Integer, Double 和 Character
        Integer[] intArray = { 1, 2, 3, 4, 5 };
        Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
        Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };
 
        System.out.println( "整型数组元素为:" );
        printArray( intArray  ); // 传递一个整型数组
 
        System.out.println( "\n浮点型数组元素为:" );
        printArray( doubleArray ); // 传递一个双精度型数组
 
        System.out.println( "\n字符型数组元素为:" );
        printArray( charArray ); // 传递一个字符型数组
    } 
}

编译以上代码,运行结果如下所示:

整型数组元素为:
1 2 3 4 5 

浮点型数组元素为:
1.1 2.2 3.3 4.4 

字符型数组元素为:
H E L L O 

有界的类型参数:可能有时候,你会想限制那些被允许传递到一个类型参数的类型种类范围。例如,一个操作数字的方法可能只希望接受Number或者Number子类的实例。这就是有界类型参数的目的。

要声明一个有界的类型参数,首先列出类型参数的名称,后跟extends关键字,最后紧跟它的上界。

实例

下面的例子演示了"extends"如何使用在一般意义上的意思"extends"(类)或者"implements"(接口)。该例子中的泛型方法返回三个可比较对象的最大值。

public class MaximumTest
{
   // 比较三个值并返回最大值
   public static <T extends Comparable<T>> T maximum(T x, T y, T z)
   {                     
      T max = x; // 假设x是初始最大值
      if ( y.compareTo( max ) > 0 ){
         max = y; //y 更大
      }
      if ( z.compareTo( max ) > 0 ){
         max = z; // 现在 z 更大           
      }
      return max; // 返回最大对象
   }
   public static void main( String args[] )
   {
      System.out.printf( "%d, %d 和 %d 中最大的数为 %d\n\n",
                   3, 4, 5, maximum( 3, 4, 5 ) );
 
      System.out.printf( "%.1f, %.1f 和 %.1f 中最大的数为 %.1f\n\n",
                   6.6, 8.8, 7.7, maximum( 6.6, 8.8, 7.7 ) );
 
      System.out.printf( "%s, %s 和 %s 中最大的数为 %s\n","pear",
         "apple", "orange", maximum( "pear", "apple", "orange" ) );
   }
}

编译以上代码,运行结果如下所示:

3, 4 和 5 中最大的数为 5

6.6, 8.8 和 7.7 中最大的数为 8.8

pear, apple 和 orange 中最大的数为 pear

泛型类

泛型类的声明和非泛型类的声明类似,除了在类名后面添加了类型参数声明部分。

和泛型方法一样,泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。因为他们接受一个或多个参数,这些类被称为参数化的类或参数化的类型。

实例

如下实例演示了我们如何定义一个泛型类:

public class Box<T> {
   
  private T t;
 
  public void add(T t) {
    this.t = t;
  }
 
  public T get() {
    return t;
  }
 
  public static void main(String[] args) {
    Box<Integer> integerBox = new Box<Integer>();
    Box<String> stringBox = new Box<String>();
 
    integerBox.add(new Integer(21));
    stringBox.add(new String("我爱你"));
 
    System.out.printf("整型值为 :%d\n\n", integerBox.get());
    System.out.printf("字符串为 :%s\n", stringBox.get());
  }
}

编译以上代码,运行结果如下所示:

整型值为 :21

字符串为 :我爱你

泛型同样可以在类中包含参数化的方法,而方法所在的类可以是泛型类,也可以不是泛型类。也就是说,是否拥有泛型方法,与其所在的类是不是泛型没有关系。

泛型方法使得该方法能够独立于类而产生变化。如果使用泛型方法可以取代类泛型化,那么就应该只使用泛型方法。另外,对一个 static 的方法而言,无法访问泛型类的类型参数。因此,如果 static 方法需要使用泛型能力,就必须使其成为泛型方法。

定义泛型方法的语法格式如下:

[访问权限修饰符] [static] [final] <类型参数列表> 返回值类型 方法名([形式参数列表])

一般来说编写 Java 泛型方法,其返回值类型至少有一个参数类型应该是泛型,而且类型应该是一致的,如果只有返回值类型或参数类型之一使用了泛型,那么这个泛型方法的使用就被限制了。下面就来定义一个泛型方法,具体介绍泛型方法的创建和使用。

例 3:定义泛型方法,参数类型使用“A”来代替。在方法的主体中打印出个人信息。代码的实现如下:

public class Test {
public static <A> void List(A book) { // 定义泛型方法
if (student != null) {
System.out.println(student);
}
}

public static void main(String[] args) {
Student stu = new Student(1, "我叫zeus", 21);
List(stu); // 调用泛型方法
  }
}

该程序中的 Student 类为前面示例中使用到的 Student类。在该程序中定义了一个名称为 List 的方法,该方法的返回值类型为 void,类型参数使用“T”来代替。在调用该泛型方法时,将一个 Student 对象作为参数传递到该方法中,相当于指明了该泛型方法的参数类型为 Student。

该程序的运行结果如下:

1, 我叫zeus,21

泛型的高级用法

泛型的用法非常灵活,除在集合、类和方法中使用外,本节将从三个方面介绍泛型的高级用法,包括限制泛型可用类型、使用类型通配符、继承泛型类和实现泛型接口。

1. 限制泛型可用类型

在 Java 中默认可以使用任何类型来实例化一个泛型类对象。当然也可以对泛型类实例的类型进行限制,语法格式如下:

class 类名称<T extends anyClass>

其中,anyClass 指某个接口或类。使用泛型限制后,泛型类的类型必须实现或继承 anyClass 这个接口或类。无论 anyClass 是接口还是类,在进行泛型限制时都必须使用 extends 关键字。

例如,在下面的示例代码中创建了一个 ListClass 类,并对该类的类型限制为只能是实现 List 接口的类。

// 限制ListClass的泛型类型必须实现List接口
public class ListClass<T extends List> {
    public static void main(String[] args) {
        // 实例化使用ArrayList的泛型类ListClass,正确
        ListClass<ArrayList> a = new ListClass<ArrayList>();
        // 实例化使用LinkedList的泛型类LlstClass,正确
        ListClass<LinkedList> b = new ListClass<LinkedList>();
        // 实例化使用HashMap的泛型类ListClass,错误,因为HasMap没有实现List接口
        // ListClass<HashMap> c=new ListClass<HashMap>();
    }
}

在上述代码中,定义 ListClass 类时设置泛型类型必须实现 List 接口。例如,ArrayList 和 LinkedList 都实现了 List 接口,所以可以实例化 ListClass 类。而 HashMap 没有实现 List 接口,所以在实例化 ListClass 类时会报错。

当没有使用 extends 关键字限制泛型类型时,其实是默认使用 Object 类作为泛型类型。因此,Object 类下的所有子类都可以实例化泛型类对象,如图 1 所示的这两种情况。

2. 使用类型通配符

Java 中的泛型还支持使用类型通配符,它的作用是在创建一个泛型类对象时限制这个泛型类的类型必须实现或继承某个接口或类。

使用泛型类型通配符的语法格式如下:

泛型类名称<? extends List>a = null;

其中,“<? extends List>”作为一个整体表示类型未知,当需要使用泛型对象时,可以单独实例化。

例如,下面的示例代码演示了类型通配符的使用。

    A<? extends List>a = null;
    a = new A<ArrayList> ();    // true
    b = new A<LinkedList> ();    // true
    c = new A<HashMap> ();    // false

在上述代码中,同样由于 HashMap 类没有实现 List 接口,所以在编译时会报错。

3. 继承泛型类和实现泛型接口

定义为泛型的类和接口也可以被继承和实现。例如下面的示例代码演示了如何继承泛型类。

    public class FatherClass<T1>{}
    public class SonClass<T1,T2,T3> extents FatherClass<T1>{}

如果要在 SonClass 类继承 FatherClass 类时保留父类的泛型类型,需要在继承时指定,否则直接使用 extends FatherClass 语句进行继承操作,此时 T1、T2 和 T3 都会自动变为 Object,所以一般情况下都将父类的泛型类型保留。

下面的示例代码演示了如何在泛型中实现接口。

    interface interface1<T1>{}
    interface SubClass<T1,T2,T3> implements
    Interface1<T2>{}

泛型的优点

1.代码可复用性

泛型使我们能够编写适用于不同类型数据的代码。例如:

public <T> void genericsMethod(T data) {...}

在这里,我们创建了一个泛型方法。此方法可用于对整数数据,字符串数据等执行操作。

2.编译时类型检查

泛型的type参数提供有关泛型代码中使用的数据类型的信息。

因此,可以在编译时识别任何错误,比运行时错误更容易修复。例如:

//不使用泛型
NormalClass list = new NormalClass();

//调用NormalClass的方法
list.display("String");

在上面的代码中,我们有一个普通的类。我们通过传递字符串数据来调用此类的名为display()的方法。

在这里,编译器不知道在参数中传递的值是否正确。但是,让我们看看如果改用泛型类会发生什么。

//使用泛型
GenericsClass<Integer> list = new GenericsClass<>();

//调用GenericsClass的方法
list2.display("String");

在上面的代码中,我们有一个泛型类。 在这里,类型参数表示该类正在处理Integer数据。因此,当字符串数据在参数中传递时,编译器将生成一个错误。 

3.泛型与集合一起使用

集合框架使用Java中的泛型概念。例如,

// 创建一个字符串类型ArrayList
ArrayList<String> list1 = new ArrayList<>();

// 创建一个整数类型ArrayList
ArrayList<Integer> list2 = new ArrayList<>();

在上面的示例中,我们使用了相同的ArrayList类来处理不同类型的数据。

类似ArrayList,其他集合(LinkedList,Queue,Maps,等等)也是Java的泛型。

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

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

相关文章

Java课题笔记~ JSP编程

4.1 JSP基本语法 JSP (全称Java Server Pages) 是由 Sun Microsystems 公司倡导和许多公司参与共同创建的一种使软件开发者可以响应客户端请求&#xff0c;而动态生成 HTML、XML 或其他格式文档的Web网页的技术标准。 JSPHTMLJava JSP的本质是Servlet 访问JSP的时候&#x…

【逗老师的PMP学习笔记】10、项目沟通管理

目录 一、规划沟通管理1、【关键工具】沟通技术2、【关键工具】沟通模型&#xff08;沟通模式&#xff09;3、【关键工具】沟通方法4、【关键工具】文化意识5、【关键输出】沟通管理计划 二、管理沟通1、【关键工具】会议管理 三、监督沟通 一、规划沟通管理 规划沟通管理是基于…

光威神策DDR5 6800超频破10GHz,国产良心价高端内存创造历史

前段时间光威把8000MHz的DDR5内存打到了千元以内&#xff0c;但是因为当时AMD平台还不支持&#xff0c;确实是馋哭了很多AMD玩家&#xff0c;这两天AMD总算发布了新版AM5主板固件&#xff0c;AGESA 1.0.0.7B BIOS&#xff0c;让AMD Ryzen 7000系列CPU和主板能够支持8000MHZ以上…

MySQL 中读写分离可能遇到的问题

前言 MySQL 中读写分离是经常用到了的架构了&#xff0c;通过读写分离实现横向扩展的能力&#xff0c;写入和更新操作在源服务器上进行&#xff0c;从服务器中进行数据的读取操作&#xff0c;通过增大从服务器的个数&#xff0c;能够极大的增强数据库的读取能力。 MySQL 中的…

JavaWeb-Servlet服务连接器(一)

目录 1.Servlet生命周期 2.Servlet的配置 3.Servlet的常用方法 4.Servlet体系结构 5.HTTP请求报文 6.HTTP响应报文 1.Servlet生命周期 Servlet&#xff08;Server Applet&#xff09;是Java Servlet的简称。其主要的功能是交互式地浏览和修改数据&#xff0c;生成一些动态…

《数据同步-NIFI系列》Nifi配置UpdateAttribute实现字符串时间戳转日期

Nifi配置UpdateAttribute实现字符串时间戳转日期 数据处理流程如下&#xff1a;查询源数据库&#xff0c;将Avro转为Json格式&#xff0c;然后使用EvaluateJsonPath修改字段名&#xff0c;最后使用replaceText将参数组成SQL&#xff0c;最后PutSQL。 一、字段串时间戳导致无法插…

章节5:脚本注入网页-XSS

章节5&#xff1a;脚本注入网页-XSS XSS &#xff1a;Cross Site Script 恶意攻击者利用web页面的漏洞&#xff0c;插入一些恶意代码&#xff0c;当用户访问页面的时候&#xff0c;代码就会执行&#xff0c;这个时候就达到了攻击的目的。 JavaScript、Java、VBScript、Activ…

web-js

<html><head><title>基本语法</title></head><body><script>// window.alert("js");// document.write("js");// console.log("js");// function add(a,b){// return ab;// }var result functio…

Linux基础与应用开发系列九:各类系统函数

open_close函数 OPEN函数 头文件&#xff1a; #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> 函数原型&#xff1a; 当文件存在时 int open(const char* pathname,int flags) 当文件不存在时 int open (const char* pathname,int f…

解决“先commit再pull”造成的git冲突

一、问题场景 在分支上修改了代码然后commit&#xff08;没有push&#xff09;&#xff0c;此时再git pull&#xff0c;拉下了别人的修改&#xff0c;但是报错无法merge 二、解决步骤 1.在idea下方工具栏选择git -> log&#xff0c;可以看到版本变化链表&#xff0c;右键…

day17 enum abstract interface 枚举 抽象 接口

一、枚举 enum 枚举本来的面目 创建Season类&#xff0c; 所有类都默认继承Object&#xff0c;写不写都一样 声明属性 &#xff1a;季节的名字、 季节的描述&#xff0c; 因为枚举的对象是看的见的客观事物&#xff0c; 想让它的属性不可修改 使用 final修饰表示最终的 &am…

2023年Q2天猫洗衣机行业品牌销售排行榜(淘宝天猫数据)

洗衣机作为普及率极高的家电之一&#xff0c;如今已经成为我们生活中不可或缺的一部分。由于洗衣机的普及率较高&#xff0c;因此虽其市场规模庞大&#xff0c;但如今要使洗衣机呈现规模化增长的可能性还是比较小的。不过&#xff0c;随着用户需求及产品的升级&#xff0c;洗衣…

Android Socket使用TCP协议实现手机投屏

本节主要通过实战来了解Socket在TCP/IP协议中充当的是一个什么角色&#xff0c;有什么作用。通过Socket使用TCP协议实现局域网内手机A充当服务端&#xff0c;手机B充当客户端&#xff0c;手机B连接手机A&#xff0c;手机A获取屏幕数据转化为Bitmap&#xff0c;通过Socket传递个…

打造专属花店展示小程序

在当今社会&#xff0c;微信小程序已经成为了各行各业拓展客户资源的利器&#xff0c;而花店行业也不例外。通过打造一个独特的花店小程序&#xff0c;你可以为你的花店带来更多的曝光和客户资源。那么&#xff0c;如何制作一个专属的花店小程序呢&#xff1f;下面我们就来一步…

领航优配:股票分红为什么股价下降?分红有什么好处?

股票分红是一种报答股东的方法&#xff0c;也是一种表现公司价值的方法。那么股票分红为什么股价下降&#xff1f;分红有什么优点&#xff1f;领航优配也为大家准备了相关内容&#xff0c;以供参考。 股票分红为什么股价下降&#xff1f; 股票进行分红后股价出现跌落是一种很常…

新疆大学841软件工程考研

1&#xff0e;软件生产的发展经历了三个阶段&#xff0c;分别是____、程序系统时代和软件工程时代时代。 2&#xff0e;可行性研究从以下三个方面研究每种解决方法的可行性&#xff1a;经济可行性、社会可行性和_____。 3&#xff0e;HIPO图的H图用于描述软件的层次关系&…

【ROS】fsd_algorithm架构学习与源码分析(致敬)

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍fsd_algorithm架构学习与源码分析。 无专精则不能成&#xff0c;无涉猎则不能通。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&am…

Git全栈体系(四)

第七章 IDEA 集成 Git 一、配置 Git 忽略文件 1. Eclipse 特定文件 2. IDEA 特定文件 3. Maven 工程的 target 目录 4. 问题 4.1 为什么要忽略他们&#xff1f; 与项目的实际功能无关&#xff0c;不参与服务器上部署运行。把它们忽略掉能够屏蔽 IDE 工具之间的差异。 4.2 …

eNSP 实现 CLI 窗口叠放

文章目录 1 问题截图2 问题解决3 扩展3.1 打开所有 CLI3.2 CLI&#xff1a;Command line interface 1 问题截图 问题描述&#xff1a;命令行窗口是分开的&#xff0c;找对应的窗口太麻烦了 2 问题解决 解决办法&#xff1a;点下图控件即可。 效果展示&#xff1a; 3 扩展 …

文档控件DevExpress Office File API v23.1新版亮点 - 支持.NET MAUI

DevExpress Office File API是一个专为C#, VB.NET 和 ASP.NET等开发人员提供的非可视化.NET库。有了这个库&#xff0c;不用安装Microsoft Office&#xff0c;就可以完全自动处理Excel、Word等文档。开发人员使用一个非常易于操作的API就可以生成XLS, XLSx, DOC, DOCx, RTF, CS…