为什么要使用泛型?

未使用泛型的情况:

// 创建列表类
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. 20155321 《网络攻防》 Exp8 Web基础

    20155321 <网络攻防> Exp8 Web基础 基础问题回答 什么是表单? 表单是主要负责数据采集功能.主要是以下三个部分构成: 表单标签:包含处理表单数据所用的程序的URL以及数据 ...

  2. 20155338 《网络攻防》Exp5 MSF基础应用

    20155338 <网络攻防>Exp5 MSF基础应用 基础问题回答 1. 用自己的话解释什么是exploit,payload,encode? exploit将真正要负责攻击的代码传送到靶 ...

  3. CLR回收非托管资源

    一.非托管资源 在<垃圾回收算法之引用计数算法>.<垃圾回收算法之引用跟踪算法>和<垃圾回收算法之引用跟踪算法>这3篇文章中,我们介绍了垃圾回收的一些基本概念和原理 ...

  4. Luogu P1196 [NOI2002]银河英雄传说

    一年没写博客了(滑稽). 这道题很玄学,导致自己都有一个坑人的问题求解.如果有大佬有能力求帮助:https://www.luogu.org/discuss/show?postid=30231 再来讲一 ...

  5. P4175 [CTSC2008]网络管理

    如果没有修改就是简单主席树,有了修改的话因为主席树维护的是到根的一段路径,所以修改操作会修改子树,也就是连续的一段dfn 所以显然树套树一波就没了 极其好写 #include<bits/stdc ...

  6. LOJ.#6468. 魔法[差分+树状数组]

    题意 题目链接 分析 将询问差分并不断加入颜色. 每种颜色,一个位置 \(p\) 都只会走到与之左右相邻的两个位置之一,分类讨论 \(\rm |A-B|\) 的符号. 实现可以使用树状数组. 总时间复 ...

  7. 详解C#7.0新特性

    1. out 变量(out variables) 以前我们使用out变量必须在使用前进行声明,C# 7.0 给我们提供了一种更简洁的语法 “使用时进行内联声明” .如下所示: 1 var input ...

  8. 阿里云centos 安装禅道

    下载 我的阿里云服务器系统是 centos6.8 64 位,下载的禅道版本是 Linux 64位一键安装包(适用于Linux 64位) 由于阿里云服务器没桌面,所以下载用不了浏览器,可考虑在本地下载后 ...

  9. C#_委托

    委托属于C#中的新名词,它的应用也非常广泛,例如事件就是委托最简单而又直接的例子. 那么首先说说什么是委托,其实委托在用过C或者C++的人看来就是函数指针,不过使用C#的大多数人都没有用过这两门语言, ...

  10. mac10.12.6系统配置clion编写CMakeLists文件运行opencv3

    按照mac10.12.6系统使用cmake安装opencv3.3.0+opencv_contrib-3.3.0下载编译安装好了文件以后,装好clion编译器,新建C++可执行工程,编写代码 opecv ...