为什么要使用泛型?

未使用泛型的情况:

// 创建列表类
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. Vultr搭建SS服务

    购买VPS VPS又叫虚拟服务器,相当于是讲物理服务器的资源进行虚拟划分然后分配给不同的用户使用. Vultr服务器按小时计费,最低0.004美元/h,算起来2.5美元/月,且destory掉服务器是 ...

  2. EZ 2018 01 14 2018noip第四次膜你赛

    这次惨烈的炸了个精光(只有20),然后对我的OI想法造成了巨大的转折. (以上有点作,其实我只是再也不用vector存图了而已(用邻接表)) 难度很不均匀,而且题型很狗(还有结论题???) T1 坑人 ...

  3. Luogu P3370 【模板】字符串哈希

    方法很多,hash,双hash(个人想到一种三hash),挂链,还有STL: map 乱搞 CODE #include<iostream> #include<map> #inc ...

  4. JavaScript快速入门-ECMAScript语句

    JavaScript语句(if.for.for in.do...while.while.break.continue.switch) 一.if语句 if (condition) statement1 ...

  5. 关于web.xml3.0启动报错

    九月 08, 2017 10:18:19 上午 org.apache.tomcat.util.digester.SetPropertiesRule begin 警告: [SetPropertiesRu ...

  6. Unity接SDK通用方法总结

    第一篇博客,回顾接UnitySDK的坑 接SDK主要有两种方式,第三方SDK和手动接(我用的android studio) 首先接触到的SDK就是Facebook-Unity的SDK,主要就是face ...

  7. UE4添加植被Foliage Type

    在UE4中的地形渲染上不可避免的需要添加植被,而如果采取手动添加StaticMesh植被的方式则会浪费大量的时间精力. UE4提供了一种批量添加地面植被类型的方式Foliage Type.在编辑器内容 ...

  8. centos crontab 计划任务 设置与查看

    centos 上 crontab 计划任务 ,这个版本解释的比较清晰 林涛 发表于:2017-4-27 11:11 分类:26点 标签:crontab,Linux,计划任务 36次 这个版本的cron ...

  9. 《Linux内核分析》第三周

    [李行之原创作品 转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000] <Linux内 ...

  10. Linux内核分析——计算机是如何工作的

    马悦+原创作品转载请注明出处+<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.计算机是如何工作的 ( ...