为什么要使用泛型?

  在Java中增加泛型之前,泛型程序设计是用继承来实现的,例如ArrayList,只维护Object引用的数组:

 public class ArrayList{
private Object[] elementData; public Object get(int i){...}
public void add(Object o){...}
}

  在这里有一个问题,就是每次使用的时候,都必须进行一次强制类型转换

 ArrayList list = new ArrayList();
...
String name = (String)list.get(0);

  可以发现还没有错误检查,如果是其他类型会检测不到,当添加的时候,可以是任何类的对象

  每次都转换一次很麻烦,用什么解决呢,类型参数!

ArrayList<String> list = new ArrayList<String>();
//在JAVASE 7以后的版本中,可以直接使用一下方法来声明了
ArrayList<String> list = new ArrayList<>();

简单泛型类

public class Pair<T>{
private T first;
private T second; public Pair(){first = null; second = null;}
public Pair(T first, T second){this.first = first; this.second = second} //get set
...
}

  现在,我们可以像现在这样定义了

Pair<String> test1 = new Pair<>();
Pair<Integer> test2 = new Pair<>();

泛型方法

public class test{
public static <T> T first(T...a){
//...
}
}

  但是需要注意的是,下面这几种,并不是泛型方法,不要混淆

 public class test<T>{
private T t; //这个不是,只是一个普通的成员的方法
public test(T t){
this.t = t;
}
//这个也不是,只是泛型类是形参
public void add(test<T> object){...}
}

通配符

  通配符是什么,为什么要使用通配符?试想一下,如果我们在一个方法中要传入的参数可能是一个类的子类,这该怎么办呢?

 public void test(Persion<Number> p){...}

 x.test(Intger); //错误
x.test(Double);

  不妨自己在编译器上试一试,这里是识别不了的,泛型中这两个没有关联关系,so,可以使用通配符

 public void test(List<? extends T>){...}

  这样子类和父类就联系起来了,?是类型实参,不是类型形参

  如果像上面这样使用,我们可以add元素进去吗?反着思考一下,一个父类派生出很多个子类,我们在实例化的时候,很可能new出来的不是一个东西,这可不行呀,要存就得存一种,哪有Int,float都存进去的道理,如果还不明白可以看看代码

 List<? extends Person> = new ArrayList<Student>();
List<? extends Person> = new ArrayList<Teacher>();

  这样的话我们只能Get,并不能add,想要add那我们现在可以这样

 public void test(List<? superT>){...}

  如此之后,只能add,不能get,又是为什么呢,还是反着思考,既然我们能够add元素进去,但是每一个元素的实际类型不相同

 List<? super Student> = new ArrayList<Student>();
List<? super Student> = new ArrayList<Person>();

  当我们想get到Student的时候,可能是一个Person类型,这个Person可能是个Teacher

说了这么多,终于到了类型擦除

类型擦除

  在JVM中不存在什么泛型,只有基本的类,当我们定义了一个泛型类的时候

 public class Node<T> {
private T data;
private Node<T> next; public Node(T data, Node<T> next) {
this.data = data;
this.next = next;
} // ...
}

  在做完类型检查后,会变成这样

 public class Node{
private Object data;
private Node next; public Node(Object data, Node next) {
this.data = data;
this.next = next;
}
// ...
}

  惊不惊喜意不意外!如果我们不想变成Object怎么办,可以自己设置

 public class Node<T extneds Comparable<T>> {
private T data;
private Node<T> next; public Node(T data, Node<T> next) {
this.data = data;
this.next = next;
}
// ...
} public class Node {
private Comparable data;
private Node next; public Node(Comparable data, Node next) {
this.data = data;
this.next = next;
}
// ...
}

  看似其实就是这样嘛!但是这样的话会引起一些问题

  1、不允许创建泛型数组

    因为数组中的元素必须统一类型,如若使用了泛型数组,类型都被擦除成Object后,我们不会知道插入的数据是否都是同一个类型的,出错很难排查,可以运行一下下面的代码

Class c1 = new ArrayList<Integer>().getClass();
Class c2 = new ArrayList<Double>().getClass();
System.out.println(c1 == c2);

    最终会得到true的结果,这里可以联想到,泛型无法使用instanceof

  2、桥方法的合成用来保持多态  

 public class Node<T> {
public T data;
public Node(T data) { this.data = data; }
public void setData(T data) {
System.out.println("Node.setData");
this.data = data;
}
}
public class MyNode extends Node<Integer> {
public MyNode(Integer data) { super(data); }
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
}

    类型擦除后

 public class Node {
public Object data;
public Node(Object data) { this.data = data; }
public void setData(Object data) {
System.out.println("Node.setData");
this.data = data;
}
}
public class MyNode extends Node {
public MyNode(Integer data) { super(data); }
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
 MyNode mn = new MyNode(5);
Node n = mn;
n.setData("Hello");

    实际上不是这样的,这会抛出ClassCastExeption,在哪里?

 class MyNode extends Node {
// 桥方法
public void setData(Object data) {
setData((Integer) data);
}
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
// ...
}

    setData方法里面有一个强制类型转换,String没办法转换成Integer,记住一句话,桥方法被合成用来保持多态

  3、反射和泛型

    反射允许你在运行时分析任意的对象,如果对象是泛型类的实例,关于泛型类型参数则得不到太多信息,因为它们会被擦除

 

  

Java泛型的基本介绍与使用的更多相关文章

  1. java泛型探索——介绍篇

    1. 泛型出现前后代码对比 先来看看泛型出现前,代码是这么写的: List words = new ArrayList(); words.add("Hello "); words. ...

  2. java泛型(一)、泛型的基本介绍和使用

    现在开始深入学习java的泛型了,以前一直只是在集合中简单的使用泛型,根本就不明白泛型的原理和作用.泛型在java中,是一个十分重要的特性,所以要好好的研究下. 泛 型的定义:泛型是JDK 1.5的一 ...

  3. Java泛型一:基本介绍和使用

    原文地址http://blog.csdn.net/lonelyroamer/article/details/7864531 现在开始深入学习java的泛型了,以前一直只是在集合中简单的使用泛型,根本就 ...

  4. java泛型 7 泛型的基本介绍和使用

    现在开始深入学习Java的泛型了,以前一直只是在集合中简单的使用泛型,根本就不明白泛型的原理和作用.泛型在java中,是一个十分重要的特性,所以要好好的研究下. 一.泛型的基本概念 泛型的定义:泛型是 ...

  5. Java泛型介绍!!!

    Java总结篇系列:Java泛型  转自:http://www.cnblogs.com/lwbqqyumidi/p/3837629.html 一. 泛型概念的提出(为什么需要泛型)? 首先,我们看下下 ...

  6. Java 泛型 介绍

    为什么需要泛型? public class GenericTest { public static void main(String[] args) { List list = new ArrayLi ...

  7. java泛型介绍

    一.泛型初衷 Java集合不会知道我们需要用它来保存什么类型的对象,所以他们把集合设计成能保存任何类型的对象,只要就具有很好的通用性.但这样做也带来两个问题: –集合对元素类型没有任何限制,这样可能引 ...

  8. Java泛型的历史

    为什么Java泛型会有当前的缺陷? 之前的章节里已经说明了Java泛型擦除会导致的问题,C++和C#的泛型都是在运行时存在的,难道Java天然不支持“真正的泛型”吗? 事实上,在Java1.5在200 ...

  9. java泛型基础

    泛型是Java SE 1.5的新特性, 泛型的本质是参数化类型, 也就是说所操作的数据类型被指定为一个参数. 这种参数类型可以用在类.接口和方法的创建中, 分别称为泛型类.泛型接口.泛型方法.  Ja ...

随机推荐

  1. NW.js开发环境的搭建

    写在前面: 之前一直在找关于在mac怎么搭建nw.js的开发环境,苦于自己也没有很深入的理解,其实看看官方文档就差不多知道mac下要怎么整了. 官方文档的图: 正题开始: 先去下载一个nw.js的安装 ...

  2. EJB结合struts2创建项目、发布jboss服务器和访问、父类(BaseDaoImpl)的封装

    一.环境搭建: 1.准备jboss服务器,将对应数据库的xml配置好放到jboss的发布目录下. <?xml version="1.0" encoding="UTF ...

  3. C# 程序启动其他进程程序

    1  启动一个独立进程,需要用到的命名空间是:using System.Diagnostics;   进程类是 Process ,进程的相关参数信息类是 ProcessStartInfo 2  等待启 ...

  4. 使用TestFlight测试时候相关内容

    前言:记录一下使用TestFlight测试时候相关内容 场景:在我们添加测试员:给测试员发送了邀请:测试员使用TestFlight的时候,其实是有崩溃的次数的记录的,相应的崩溃的信息也是可以查询到的. ...

  5. 整理 45 道 CSS 基础面试题(附答案)

    1.介绍一下标准的CSS的盒子模型?与低版本IE的盒子模型有什么不同的? 标准盒子模型:宽度=内容的宽度(content)+ border + padding + margin低版本IE盒子模型:宽度 ...

  6. webpack的基本配置和一些理解

    最近花了两周的休息时间学习了webpack,能够可以编写自己项目所需要的配置文件,总体来说webpack是一种非常优秀的前端模块化的打包工具,非常值得花时间来研究学习. 什么是webpack,它的出现 ...

  7. css的基础用法(下)

    定位: <html> <head> <meta charset="utf-8" /> <title>定位</title> ...

  8. Oracle单行函数用法

    单行函数分为五种类型:字符函数.数值函数.日期函数.转换函数.通用函数. 1.字符函数: 对于输入的字符转换为需要转为的字符或数值. upper()大写 --小写字母转为大写字母 --对于表指定的字符 ...

  9. oracle中lock和latch的用途

    本文向各位阐述Oracle的Latch机制,Latch,用金山词霸翻译是门插栓,闭锁,专业术语叫锁存器,我开始接触时就不大明白为什么不写Lock,不都是锁吗?只是翻译不同而以?研究过后才知道两者有很大 ...

  10. js的事件流你真的弄明白了吗?

    当浏览器发展到第四代时候,浏览器开发团队遇到了一个有意思的问题:页面的哪一部分会拥有某个特地的事件?要明白这个问题问的是什么,可以想象画在纸上的一组同心圆,如果你把手指放在圆心上,那么你的手指指向的不 ...