为什么要使用泛型?

未使用泛型的情况:

// 创建列表类
List list = new ArrayList();
// 添加一个类型为 String 的列表元素
list.add("hello");
// 强制转换为 String 类型,再赋值给类型为 s 的引用变量
String s = (String) list.get(0);

使用泛型的情况:

// 创建泛型类,<String> 为类型参数
List<String> list = new ArrayList<String>();
// 添加一个类型为 String 的列表元素
list.add("hello");
// 这里不需要强制类型转换
String s = list.get(0);

好处:实现通用的泛型算法,处理不同类型的集合,可以自定义类型,类型安全,便于阅读。

泛型类型

一个泛型类型是一个类型参数化(<类型参数>)的泛型类或接口。

一个简单的 Box 类

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

Box 类中方法接受或返回一个对象,除了基本类型外你可以传入任何对象。编译时无法检查类是如何使用的,如果传入一个 Integer 并希望获取 Integer,但却错误传入 String 对象,那么就会导致运行错误。

泛型版本 Box 类

泛型类型定义格式:

class name<T1, T2, ..., Tn> { /* ... */ } //T1, T2, ..., Tn为类型参数

类型参数(也成为类型变量)跟在类名称后面,类型参数放在尖括号(<>)中(T1, T2, ..., and Tn),

修改后的泛型 Box 类:

/**
* Box 类的泛型版本
* @param <T> 类型的值被装箱
*/
public class Box<T> {
// T 表示 "类型"
private T t; public void set(T t) { this.t = t; }
public T get() { return t; }
}

上面代码中,Box 类中的所有 Object 类型都被替换为 T 类型,类型变量可以是指定的任何非基本类型:任何类类型、任何接口类型、任何数组类型,甚至是任何其他的类型变量。

泛型技术也可以实现通用的泛型接口。

类型参数的命名约定

按照惯例,类型参数名为单个大写字母。这与已知的变量命名约定形成鲜明对比,这样做有充分的理由:如果没有这个约定,那么很难区分类型变量和普通类或接口名称之间的区别。

最常用的类型参数名称:

  • E - Element (used extensively by the Java Collections Framework)
  • K - Key
  • N - Number
  • T - Type
  • V - Value
  • S,U,V etc. - 2nd, 3rd, 4th types

调用和实例化泛型类型

用具体的类型替换类型变量 T 就可以实例化泛型类型,例如:

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

通用类型的调用一般称为“参数化类型”。

像往常一样使用new关键字实例化,但在类名和括号之间放置<Integer>:

在 Java SE 7 和更高的版本中,只要编译器可以根据上下文确定或推断类型参数,就可以用一组空类型参数(<>)替换调用泛型类的构造函数所需的类型参数。例如:

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

多类型参数

泛型类可以拥有多个类型参数。例如,实现通用 Pair 接口的通用 OrderedPair 类:

public interface Pair<K, V> {
public K getKey();
public V getValue();
} public class OrderedPair<K, V> implements Pair<K, V> { private K key;
private V value; public OrderedPair(K key, V value) {
this.key = key;
this.value = value;
} public K getKey() { return key; }
public V getValue() { return value; }
}

以下语句创建 OrderedPair 类的两个实例:

Pair <String,Integer> p1 = new OrderedPair <String,Integer>(“Even”,8);
Pair <String,String> p2 = new OrderedPair <String,String>(“hello”,“world”);

新的OrderedPair <String,Integer>代码将 K 实例化为一个字符串,将 V 实例化为一个整数。因此,OrderedPair 的构造函数的参数类型分别是 String 和 Integer。由于自动装箱,将 String 和 int 传递给类是有效的。

正如前文所述,由于Java编译器可以从OrderedPair <String,Integer>声明中推断 K 和 V 类型,因此可以使用以下缩写:

OrderedPair <String,Integer> p1 = new OrderedPair <>(“Even”,8);
OrderedPair <String,String> p2 = new OrderedPair <>(“hello”,“world”);

参数化类型

您也可以用参数化类型(即List )替换类型参数(即 K 或 V)。例如,使用OrderedPair <K,V>示例:

OrderedPair <String,Box <Integer>> p = new OrderedPair <>(“primes”,new Box <Integer>(...));

原始类型

原始类型是没有任何类型参数的泛型类或接口的名称。

例如,给定一个 Box 泛型类:

public class Box<T> {
public void set(T t) { /* ... */ }
// ...
}

要创建参数化类型,为形式类型参数 T 提供实际类型参数:

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

如果省略实际类型参数,则会创建一个Box<T>的原始类型:

Box rawBox = new Box();

因此,Box 是泛型Box<T>的原始类型。但是,非泛型类或接口类型不是原始类型。

在 JDK5.0 之前很多 API 类(如 Collections 类)不是通用的,为了向后兼容,允许将参数化类型分配给其原始类型:

Box<String> stringBox = new Box<>();
Box rawBox = stringBox; // OK

但是,将原始类型分配给参数化类型,编译器会发出一个警告:

Box rawBox = new Box();           // rawBox 是 Box<T> 的原始类型
Box<Integer> intBox = rawBox; // warning: unchecked conversion

如果使用原始类型来调用相应泛型类型中定义的泛型方法,则还会收到警告:

Box<String> stringBox = new Box<>();
Box rawBox = stringBox;
rawBox.set(8); // warning: unchecked invocation to set(T)

该警告显示原始类型绕过了泛型类型检查,将不安全代码的捕获推迟到运行时。因此,你应该避免使用原始类型。

Java 泛型类型基础的更多相关文章

  1. 一个小栗子聊聊JAVA泛型基础

    背景 周五本该是愉快的,可是今天花了一个早上查问题,为什么要花一个早上?我把原因总结为两点: 日志信息严重丢失,茫茫代码毫无头绪. 对泛型的认识不够,导致代码出现了BUG. 第一个原因可以通过以后编码 ...

  2. Java以基础类库

    Java以基础类库JFC(Java Foundation Class)的形式为程序员提供编程接口API,类库中的类按照用途归属于不同的包中. (一)java.lang包 Java最常用的包都属于该包, ...

  3. Java SE 基础:注释

    Java SE 基础:注释 一.注释定义 用于解释说明程序的文字. 二.注释的作用 1.解释说明程序,提高程序的可读性 2.可以帮助我们调试程序:通过注释代码块,检测错误位置 三.格式 1.单行注释 ...

  4. Java SE 基础:标识(zhì)符

    Java SE 基础:标识(zhì)符 一.标识符定义 标识符,就是给类.接口.方法.变量等起名字时的字符序列 二.组成规则与注意事项 1.组成规则 英文大小写字母 数组 $ 和 _ 2.注意事项 不 ...

  5. Java SE 基础:常用关键字

    Java SE 基础:常用关键字 常用关键字表

  6. java IO基础操作

    java IO基础,通熟易懂,好久没复习java 基础了.这里是传送门... http://www.cnblogs.com/nerxious/archive/2012/12/15/2818848.ht ...

  7. Java学习总结(二)----Java语言基础

    1.     Java语言基础 2.1 关键字 定义:被java语言赋予特殊含义的单词 特点:关键字中的字母都为小写 用于定义数据类型的关键字 class,interface,byte,short,i ...

  8. [转]Java多线程干货系列—(一)Java多线程基础

    Java多线程干货系列—(一)Java多线程基础 字数7618 阅读1875 评论21 喜欢86 前言 多线程并发编程是Java编程中重要的一块内容,也是面试重点覆盖区域,所以学好多线程并发编程对我们 ...

  9. Java 入门基础

    第零章 开始学习Java 1.Java基础最重要 Java学习中,Java的基础.Java面向对象是最关键的,而一些像框架技术等都是建立在基础之上东西. 多多处理问题,积累处理问题的能力. Java框 ...

随机推荐

  1. HTML基础之JS

    HTML中的三把利器的JS 又称为JavaScript,看着好像和Java有点联系,实际上他和java半毛钱关系都没有,JavaScript和我们学习的Python.Go.Java.C++等,都是一种 ...

  2. springboot项目生成jar包(带静态资源)方法

    [Maven]在pom.xml文件中使用resources插件的小作用 不过war包比较实用,毕竟独立的tomcat比较好控制

  3. JS跨浏览器的事件处理

    1. 事件流 事件:用户或浏览器自身执行的某种动作.如click(点击事件).mouse***(鼠标事件). 事件流:页面中接收事件的顺序,也可理解为事件在页面中传播的顺序. DOM事件流包括三个阶段 ...

  4. [arc076F]Exhausted?[霍尔定理+线段树]

    题意 地上 \(1\) 到 \(m\) 个位置摆上椅子,有 \(n\) 个人要就座,每个人都有座位癖好:选择 \(\le L\) 或者 \(\ge R\) 的位置.问至少需要在两边添加多少个椅子能让所 ...

  5. Synchronous/Asynchronous:任务的同步异步,以及asynchronous callback异步回调

    两个线程执行任务有同步和异步之分,看了Quora上的一些问答有了更深的认识. When you execute something synchronously, you wait for it to ...

  6. jersey2 整合 spring + hibernate + log4j2

    整合 spring jersey2 官方还未正式支持 spring4, 但网上有好多支持方案,折腾了一圈后,还是用了 spring3; pom 添加以下依赖配置 <!-- Spring --&g ...

  7. 关于Unity物理事件的执行顺序的最新理解

    物体A: public class A:{ B b; void FixedUpdate(){ if(input.GetKeyDow(Keycode.I)) { collider.enable=fals ...

  8. sqli-labs 下载、安装

    sqli-labs 下载.安装 下载地址:https://github.com/Audi-1/sqli-labs phpstudy:http://down.php.cn/PhpStudy2018021 ...

  9. [T-ARA][Tic Tic Toc]

    歌词来源:http://music.163.com/#/song?id=22704478 Tic Tic Toc RA Tic Tic Toc RA [Tic Tic Toc RA Tic Tic T ...

  10. 初识kibana

    前言: 什么是Kibana?? Kibana是一个开源的分析与可视化平台,设计出来用于和Elasticsearch一起使用的.你可以用kibana搜索.查看.交互存放在Elasticsearch索引里 ...