当前位置: 首页 > news >正文

Java:泛型

一、泛型的简单介绍

1. 泛型的引入

      JDK 1.5中引入了泛型这个新特性,泛型的本质是参数化类型(Parameterized Types)的应用,也就是指操作的数据类型被指定为一个参数,之后使用到该数据时必须符合指定的类型。这种参数化类型可以在类、接口和方法中使用,分别称为泛型类、泛型接口和泛型方法。

 

2. 为什么引入泛型

      泛型思想在C++模板(Templates)中开始萌芽,在Java还没有泛型的时候,只能通过Object类型是所有类型的父类型和类型强制转换两个特点的配合使用来实现类型泛化。例如在哈希表的存取中,JDK 1.5之前使用HashMap的 get( ) 方法,返回值就是一个Object对象。由于Java中所有的类型都继承自java.lang.Object,那Object可能转换为任何类型的对象。这就带来了一个缺陷:在编译期间,编译器无法检查这个Object的强制转换是否成功,只有程序员和编译期间的虚拟机才知道这个Object到底是什么类型的对象。于是导致运行期间的ClassCastException风险增加。

 

3. Java的“伪泛型”

在说明Java为什么是“伪泛型”之前,先简单的介绍一下C#的“真实泛型”:

      C#泛型是C# 2.0版本中新增的特性,C#无论语言层面还是CLR都提供对泛型的支持,所以C#泛型类或方法编译成Microsoft中间语言(MSIL)时,它依旧包含将其表示为具有类型参数的元数据,也就是说泛型是真实存在的。类型没有变成原始类型,而是通过类型膨胀实现,在运行期生成自己的虚方法和类型数据。所以C# 是“真实泛型”。

      而Java的中的泛型只存在于源程序中,在编译后的字节码中,泛型就被替换为原始类型(Raw Type),并且在相应的地方插入了强制转换代码。因此,对于运行期的Java来说,ArrayList<int>与ArrayList<String>就是同一个类。所以说泛型是Java的一颗语法糖,Java中泛型实现的方法称为类型擦除,基于这种方法实现的泛型称为“伪泛型”。

 

4. 泛型中基本术语

以 ArrayList<E> 和 ArrayList<Integer> 为例:

(1)“ArrayList<E>”称为泛型类型

(2)ArrayList<E> 中的“E”称为类型变量类型参数

(3)“ArrayList<Integer>”称为参数化类型

(4)ArrayList<Integer>中的“Integer”称为类型参数的实例,或实际类型参数

(5)ArrayList<Integer>中的“<Integer>”读作“type of Integer”

(6)“ArrayList”称为原始类型

 

5. 类型参数的命名惯例

通过这些类型参数的命名惯例,方便我们理解不同类型参数之间的区别与含义

(1)E - Element

(2)K - Key

(3)N - Number

(4)T - Type

(5)V - Value

(6)S,U,V - 第二个、第三个、第四个Type

 

二、 泛型的使用

 1. 泛型类的定义和使用

(1)泛型类(generic class)是具有一个或多个类型变量的类。定义一个泛型类只需在类名后面加上“<>”,然后在尖括号里面加上类型参数。下面是非泛型类和泛型类代码实现的简单对比:

 
// 非泛型Box类
public class Box {
    private Object object;

    public void set(Object object) { this.object = object; }
    public Object get() { return object; }
}  

由于Box类接收和返回的是Object,所以你可以传任何类型的值。假如你传了一个Integer类型的值,但是你想返回一个String类型的值,就会发生运行时错误。

 

/**
 * 泛型Box类
 * @param <T>是 Box类内部值的类型
 */
public class Box<T> {
    // T stands for "Type"
    private T t;

    public void set(T t) { this.t = t; }
    public T get() { return t; }
}

  

Box泛型类使用类型参数T代替了Object,一个类型参数可以是任何非原始类型(non-primitive type),比如,任何class type,inteface type,array type,或是一个类型参数

 

 (2)调用并实例化泛型类
调用并实例化一个泛型类,只需使用任意具体类型代替泛型类型“T”即可,比如 Integer:

Box<Integer> integerBox = new Box<Integer>();

 

2. 泛型接口的定义和使用

泛型接口的定义和使用与泛型类相似

import java.util.Date;

interface Show<T, U> {
    void show(T t, U u);
}

class ShowTest implements Show<String, Date> {
    public static void main(String[] args) {
        ShowTest test = new ShowTest();
        test.show("Time is", new Date());
    }

    public void show(String str, Date date) {
        System.out.println(str + " " + date);
    }

}

 运行结果:

 

3. 泛型方法的定义和使用

泛型类在多个方法签名间实施类型约束。在 List<V> 中,类型参数 V 出现在 get()、add()、contains() 等方法的签名中。当创建一个 Map<K, V> 类型的变量时,您就在方法之间宣称一个类型约束。您传递给 add() 的值将与 get() 返回的值的类型相同。

类似地,之所以声明泛型方法,一般是因为您想要在该方法的多个参数之间宣称一个类型约束。

public static void main(String[] args) throws ClassNotFoundException {  
        String str=get("Hello", "World");  
        System.out.println(str);  
    }  
  
    public static <T, U> T get(T t, U u) {  
        if (u != null)  
            return t;  
        else  
            return null;  
    }  

 

三、 泛型变量的类型限定

在上面,我们简单的学习了泛型类、泛型接口和泛型方法。我们都是直接使用<T>这样的形式来完成泛型类型的声明。

有的时候,类、接口或方法需要对类型变量加以约束。看下面的例子:

有这样一个简单的泛型方法:

public static <T> T get(T t1,T t2) {  
        if(t1.compareTo(t2)>=0);//编译错误  
             return t1;  
 }  

因为,在编译之前,也就是我们还在定义这个泛型方法的时候,我们并不知道这个泛型类型T,到底是什么类型,所以,只能默认T为原始类型Object。所以它只能调用来自于Object的那几个方法,而不能调用compareTo方法。

可我的本意就是要比较t1和t2,怎么办呢?这个时候,就要使用类型限定,对类型变量T设置限定(bound)来做到这一点。

我们知道,所有实现Comparable接口的方法,都会有compareTo方法。所以,可以对<T>做如下限定:

public static <T extends Comparable> T get(T t1,T t2) { //添加类型限定
          if(t1.compareTo(t2)>=0);
		return t1;
}

类型限定在泛型类、泛型接口和泛型方法中都可以使用,不过要注意下面几点:

(1)不管该限定是类还是接口,统一都使用关键字 extends

(2)可以使用&符号给出多个限定,例如

    public static <T extends Comparable&Serializable> T get(T t1,T t2)

(3)如果限定既有接口也有类,那么类必须只有一个,并且放在首位置,例如

public static <T extends Object&Comparable&Serializable> T get(T t1,T t2)

  


参考

https://docs.oracle.com/javase/tutorial/java/generics/types.html

http://blog.csdn.net/lonelyroamer/article/details/7864531 

 

 

转载于:https://www.cnblogs.com/huiAlex/p/8284723.html

相关文章:

  • Myth源码解析系列之一-项目简介
  • 在地铁上看了zabbix 的书发现 报警执行远程命令
  • Python中级 —— 01面向对象进阶
  • Ansible批量修改root密码(playbook)
  • 健忘?科学家想用机器学习+电击实验,帮你增强记忆力
  • mysql 设置自增id起始值
  • 多迪技术总监告诉你为什么人工智能用Python?
  • python之路----面向对象的封装特性
  • DAY9-字符串笔记整理2018-1-19
  • 新建一个虚拟机
  • Android layer-list的属性和使用具体解释
  • 安全地关闭 jvm(tomcat停止钩子事件处理)
  • 智慧医疗“验血查癌”或会实现
  • shell脚本练习题
  • 沉浸式状态栏解析
  • 【跃迁之路】【585天】程序员高效学习方法论探索系列(实验阶段342-2018.09.13)...
  • angular2 简述
  • Date型的使用
  • Docker容器管理
  • JavaScript-Array类型
  • js写一个简单的选项卡
  • mongodb--安装和初步使用教程
  • PHP 小技巧
  • SpriteKit 技巧之添加背景图片
  • TypeScript实现数据结构(一)栈,队列,链表
  • vuex 学习笔记 01
  • 分布式任务队列Celery
  • 观察者模式实现非直接耦合
  • 马上搞懂 GeoJSON
  • 批量截取pdf文件
  • 前端相关框架总和
  • 如何编写一个可升级的智能合约
  • 微服务入门【系列视频课程】
  • 在Docker Swarm上部署Apache Storm:第1部分
  • media数据库操作,可以进行增删改查,实现回收站,隐私照片功能 SharedPreferences存储地址:
  • FaaS 的简单实践
  • Mac 上flink的安装与启动
  • ​Linux·i2c驱动架构​
  • #设计模式#4.6 Flyweight(享元) 对象结构型模式
  • #微信小程序(布局、渲染层基础知识)
  • $.ajax()方法详解
  • (2.2w字)前端单元测试之Jest详解篇
  • (C++)八皇后问题
  • (分类)KNN算法- 参数调优
  • (附源码)springboot教学评价 毕业设计 641310
  • (附源码)springboot美食分享系统 毕业设计 612231
  • (续)使用Django搭建一个完整的项目(Centos7+Nginx)
  • (学习日记)2024.04.04:UCOSIII第三十二节:计数信号量实验
  • (原創) 博客園正式支援VHDL語法著色功能 (SOC) (VHDL)
  • (转)iOS字体
  • (转载)Linux网络编程入门
  • .htaccess 强制https 单独排除某个目录
  • .NET 4.0中使用内存映射文件实现进程通讯
  • .NET Core Web APi类库如何内嵌运行?
  • .NET Core WebAPI中封装Swagger配置