为什么要使用泛型?

未使用泛型的情况:

// 创建列表类
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. Python+Selenium爬取动态加载页面(2)

    注: 上一篇<Python+Selenium爬取动态加载页面(1)>讲了基本地如何获取动态页面的数据,这里再讲一个稍微复杂一点的数据获取全国水雨情网.数据的获取过程跟人手动获取过程类似,所 ...

  2. Wpf(Storyboard)动画简单实例

    原文:Wpf(Storyboard)动画简单实例 动画的三种变换方式 RotateTransform:旋转变换变化值:CenterX围绕转的圆心横坐标 CenterY纵坐标 Angle旋转角度(角度正 ...

  3. 小兔博客新增源码下载模块,JavaWeb项目实战,JavaScript入门教程 ,JavaSE案例等

    从今以后,所有的源码在 http://www.xiaotublog.com/downloadView.html 都可以免费下载,在下载页面还可以直接链接到相关的教程地址(如果有教程的话...). 最近 ...

  4. mfc 线程的诞生和死亡

    知识点:  线程概念  线程的诞生  线程的死亡 一. 线程: 线程,是程序执行流的最小单元. 另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点 ...

  5. python 回溯法 子集树模板 系列 —— 1、8 皇后问题

    问题 8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行.同一列或同一斜线上,问有多少种摆法. 分析 为了简化问题,考虑到8个皇后不同行,则每一行放置一个皇后,每一行的 ...

  6. Codeforces 954D Fight Against Traffic(BFS 最短路)

    题目链接:Fight Against Traffic 题意:有n个点个m条双向边,现在给出两个点S和T并要增加一条边,问增加一条边且S和T之间距离不变短的情况有几种? 题解:首先dfs求一下S到其他点 ...

  7. 浅谈android Service和BroadCastReceiver

    1.题记 Android中的服务和windows中的服务是类似的东西,服务一般没有用户操作界面,它运行于系统中不容易被用户发觉,可以使用它开发如监控之类的程序. 广播接收者(BroadcastRece ...

  8. 从头到尾谈一下HTTPS

    引言 “你能谈一下HTTPS吗?” “一种比HTTP安全的协议.” “...” 如果面试这样说的话那差不多就gg了,其实HTTPS要展开回答的话内容还挺丰富的.本篇文章详细介绍了HTTPS是什么.为什 ...

  9. Azkaban集群部署

    一.部署模式 solo-server模式  (使用内置h2存储元数据): two-server模式 (1个webServer,1个execServer在同一服务器上,使用mysql存储元数据): mu ...

  10. LintCode——第K大元素

    第K大元素:在数组num中找到第k大的元素(可以交换数组中的元素的位置) 样例: 数组 [9,3,2,4,8],第三大的元素是 4 数组 [1,2,3,4,5],第一大的元素是 5,第二大的元素是 4 ...