在Java SE1.5中。添加了一个新的特性:泛型(日本语中的总称型)。何谓泛型呢?通俗的说。就是泛泛的指定对象所操作的类型。而不像常规方式一样使用某种固定的类型去指定。

泛型的本质就是将所操作的数据类型參数化,也就是说,该数据类型被指定为一个參数。这样的參数类型能够使用在类、接口以及方法定义中。

一、为什么使用泛型呢?

在以往的J2SE中,没有泛型的情况下,一般是使用Object类型来进行多种类型数据的操作。这个时候操作最多的就是针对该Object进行数据的强制转换。而这样的转换是基于开发人员对该数据类型明白的情况下进行的(比方将Object型转换为String型)。倘若类型不一致。编译器在编译过程中不会报错,但在执行时会出错。

使用泛型的优点在于,它在编译的时候进行类型安全检查,而且在执行时全部的转换都是强制的,隐式的,大大提高了代码的重用率。

二、泛型的简单样例:

首先。我们来看看以下两个普通的class定义


public class getString {

   private String myStr;

   public String getStr() {

     return myStr;

   }
   public void setStr(str) {      myStr = str;    } }  public class getDouble {    private Double myDou;     public Double getDou() {     return myDou;    }
   public void setDou(dou) {     myDou = dou;    } }

这两个class除了所操作的数据类型不一致,其它机能都是同样的。如今,我们能够使用泛型来将上面两个class合并为一个。从而提高代码利用率。降低代码量。

public class getObj<T> {

private T myObj ;

public T getObj() {

return myObj;

}

public void setObj<T obj> {

myObj = obj;

}

}

那么,使用了泛型后。怎样生成这个class的实例来进行操作呢?请看以下的代码:

getObj<String> strObj = new getObj<String>();

strObj.setObj(“Hello Nissay”);

System.out.println(strObj.getObj());

getObj<Double> douObj = new getObj<Double>();

douObj.setObj(new Double(“116023”));

System.out.println(douObj.getObj());

三、样例分析

如今我们来分析上面那段代码:

1、<T>是泛型的标记,当然能够使用别的名字,比方。使用<T>声明一个泛型的引用,从而能够在class、方法及接口中使用它进行数据定义,參数传递。

2、<T>在声明的时候相当于一个有意义的数据类型,编译过程中不会错误发生。在实例化时,将其用一个详细的数据类型进行替代,从而就能够满足不用需求。

四、泛型的规则和限制

通过上述的样例,我们简单理解了泛型的含义。在使用泛型时,请注意其使用规则和限制,例如以下:

1、泛型的參数类型仅仅能是引用类型,而不能是简单类型。

比方。<int>是不可使用的。

2、能够声明多个泛型參数类型,比方<T, P,Q…>。同一时候还能够嵌套泛型。比如:<List<String>>

3、泛型的參数类型能够使用extends语句,比如<T extends superclass>。

4、泛型的參数类型能够使用super语句。比如< T super childclass>。

5、泛型还能够使用通配符,比如<? extends ArrayList>

五、扩展

1、extends语句

使用extends语句将限制泛型參数的适用范围。比如:

<T extends collection> ,则表示该泛型參数的使用范围是全部实现了collection接口的calss。假设传入一个<String>则程序编译出错。

2、super语句

super语句的作用与extends一样,都是限制泛型參数的适用范围。

差别在于,super是限制泛型參数仅仅能是指定该class的上层父类。

比如<T super List>。表示该泛型參数仅仅能是List和List的上层父类。

3、通配符

使用通配符的目的是为了解决泛型參数被限制死了不能动态依据实例来确定的缺点。

举个样例:public class SampleClass < T extends S> {…}

假如A。B,C。…Z这26个class都实现了S接口。我们使用时须要使用到这26个class类型的泛型參数。

那实例化的时候怎么办呢?依次写下

SampleClass<A> a = new SampleClass();

SampleClass<B> a = new SampleClass();

SampleClass<Z> a = new SampleClass();

这显然非常冗余,还不如使用Object而不使用泛型,呵呵,是吧?

别着急,咱们使用通配符,就OK了。

SampleClass<? Extends S> sc = new SampleClass();

仅仅须要声明一个sc变量。非常方便把!

通配符进阶

泛型最复杂的部分是对通配符的理解。我们将讨论三种类型的通配符以及它们的用途。

首先让我们了解一下数组是怎样工作的。能够从一个Integer[]为一个Number[]赋值。

假设尝试把一个Float写到Number[]中,那么能够编译。但在执行时会失败。出现一个ArrayStoreException:

Integer[] ia = new Integer[5];

Number[] na = ia;

na[0] = 0.5; // compiles, but fails at runtime

假设试图把该例直接转换成泛型。那么会在编译时失败,由于赋值是不被同意的:

List<Integer> iList = new ArrayList<Integer>();

List<Number> nList = iList; // not allowed

nList.add(0.5);

假设使用泛型,仅仅要代码在编译时没有出现警告,就不会遇到执行时ClassCastException。

上限通配符

我们想要的是一个确切元素类型未知的列表,这一点与数组是不同的。

List<Number>是一个列表,其元素类型是详细类型Number。

List<? extends Number>是一个确切元素类型未知的列表。它是Number或其子类型。

上限

假设我们更新初始的样例。并赋值给List<? extends Number>,那么如今赋值就会成功了:

List<Integer> iList = new ArrayList<Integer>();

List<? extends Number> nList = iList;

Number n = nList.get(0);

nList.add(0.5); // Not allowed

我们能够从列表中得到Number,由于不管列表的确切元素类型是什么(Float、Integer或Number)。我们都能够把它赋值给Number。

我们仍然不能把浮点类型插入列表中。

这会在编译时失败,由于我们不能证明这是安全的。假设我们想要向列表中加入浮点类型。它将破坏iList的初始类型安全——它仅仅存储Integer。

通配符给了我们比数组很多其它的表达能力。

为什么使用通配符

在以下这个样例中,通配符用于向API的用户隐藏类型信息。

在内部。Set被存储为CustomerImpl。而API的用户仅仅知道他们正在获取一个Set。从中能够读取Customer。

此处通配符是必需的。由于无法从Set<CustomerImpl>向Set<Customer>赋值:

public class CustomerFactory {

    private Set<CustomerImpl> _customers;

    public Set<? extends Customer> getCustomers() {

        return _customers;

    }

}

无界通配符

最后。List<?

>列表的内容能够是不论什么类型。并且它与List<? extends Object>差点儿同样。能够随时读取Object。可是不能向列表中写入内容。

公共API中的通配符

总之,正如前面所说,通配符在向调用程序隐藏实现细节方面是很重要的。但即使下限通配符看起来是提供仅仅读訪问,因为remove(int position)之类的非泛型方法,它们也并不是如此。假设您想要一个真正不变的集合,能够使用java.util.Collection上的方法。比方unmodifiableList()。

编写API的时候要记得通配符。通常,在传递泛型类型时,应该尝试使用通配符。它使很多其它的调用程序能够訪问API。

通过接收List<? extends Number>而不是List<Number>,以下的方法能够由很多不同类型的列表调用:

void removeNegatives(List<? extends Number> list);

构造泛型类型

如今我们将讨论构造自己的泛型类型。我们将展示一些样例,当中通过使用泛型能够提高类型安全性,我们还将讨论一些实现泛型类型时的常见问题。

集合风格(Collection-like)的函数

第一个泛型类的样例是一个集合风格的样例。

Pair有两个类型參数,并且字段是类型的实例:

public final class Pair<A,B> {

    public final A first;

    public final B second;

public Pair(A first, B second) {

        this.first = first;

        this.second = second;

    }

}

这使从方法返回两个项而无需为每一个两种类型的组合编写专用的类成为可能。还有一种方法是返回Object[]。而这样是类型不安全或者不整洁的。

在以下的使用方法中,我们从方法返回一个File和一个Boolean。

方法的client能够直接使用字段而无需类型强制转换:

public Pair<File,Boolean> getFileAndWriteStatus(String path){

    // create file and status

    return new Pair<File,Boolean>(file, status);

}

Pair<File,Boolean> result = getFileAndWriteStatus("...");

File f = result.first;

boolean writeable = result.second;

集合之外

在以下这个样例中,泛型被用于附加的编译时安全性。通过把DBFactory类參数化为所创建的Peer类型。您实际上是在强制Factory子类返回一个Peer的特定子类型:

public abstract class DBFactory<T extends DBPeer> {

    protected abstract T createEmptyPeer();

    public List<T> get(String constraint) {

        List<T> peers = new ArrayList<T>();

        // database magic

        return peers;

    }

}

通过实现DBFactory<Customer>,CustomerFactory必须从createEmptyPeer()返回一个Customer:

public class CustomerFactory extends DBFactory<Customer>{

public Customer createEmptyPeer() {

        return new Customer();

    }

}

泛型方法

无论想要对參数之间还是參数与返回类型之间的泛型类型施加约束,都能够使用泛型方法:

比如,假设编写的反转函数是在位置上反转。那么可能不须要泛型方法。

然而,假设希望反转返回一个新的List,那么可能会希望新List的元素类型与传入的List的类型同样。在这样的情况下,就须要一个泛型方法:

<T> List<T> reverse(List<T> list)

详细化

当实现一个泛型类时,您可能想要构造一个数组T[]。由于泛型是通过擦除(erasure)实现的,所以这是不同意的。

您能够尝试把Object[]强制转换为T[]。但这是不安全的。

详细化解决方式

依照泛型教程的惯例,解决方式使用的是“类型令牌”,通过向构造函数加入一个Class<T>參数,能够强制client为类的类型參数提供正确的类对象:

public class ArrayExample<T> {

    private Class<T> clazz;

public ArrayExample(Class<T> clazz) {

        this.clazz = clazz;

    }

public T[] getArray(int size) {

        return (T[])Array.newInstance(clazz, size);

    }

}

为了构造ArrayExample<String>,client必须把String.class传递给构造函数,由于String.class的类型是Class<String>。

拥有类对象使构造一个具有正确元素类型的数组成为可能。

Java 泛型具体解释的更多相关文章

  1. java泛型具体解释

    为什么引入泛型 bug是编程的一部分,我们仅仅能尽自己最大的能力降低出现bug的几率,可是谁也不能保证自己写出的程序不出现不论什么问题. 错误可分为两种:编译时错误与执行时错误.编译时错误在编译时能够 ...

  2. JAVA泛型解释

    理解Java泛型最简单的方法是把它看成一种便捷语法,能节省你某些Java类型转换(casting)上的操作: 1 List<Apple> box = ...; 2 Apple apple ...

  3. Java:泛型基础

    泛型 引入泛型 传统编写的限制: 在Java中一般的类和方法,只能使用具体的类型,要么是基本数据类型,要么是自定义类型.如果要编写可以应用于多种类型的代码,这种刻板的限制就会束缚很多! 解决这种限制的 ...

  4. java泛型基础

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

  5. 浅谈Java泛型之<? extends T>和<? super T>的区别

    关于Java泛型,这里我不想总结它是什么,这个百度一下一大堆解释,各种java的书籍中也有明确的定义,只要稍微看一下就能很快清楚.从泛型的英文名字Generic type也能看出,Generic普通. ...

  6. Java 泛型,了解这些就够用了。

    此文目录: Java泛型是什么? 通常的泛型的写法示例 类型擦除 为什么要使用Java泛型 通过示例了解PECS原则 一.Java泛型是什么? 官方定义 泛型是Java SE 1.5的新特性,泛型的本 ...

  7. Java泛型总结

    1. 什么是泛型?泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类.可以把类型参数看作是使用参数化类型时指定的类型的 ...

  8. java泛型的讲解

    java泛型 什么是泛型? 泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类.可以把类型参数看作是使用参数化类型时指 ...

  9. Java泛型:类型擦除

    类型擦除 代码片段一 Class c1 = new ArrayList<Integer>().getClass(); Class c2 = new ArrayList<String& ...

随机推荐

  1. C#2.0--集合--转载车老师

    集合在编程的过程中用的是非常的多,如GridViewRowCollection.ConnectionStringSettingsCollection.NameValueCollection等等.一般来 ...

  2. asp.net2.0安全性(1)--用户角色篇(起篇)--转载来自车老师

    安全管理的解决方案在.net1.1中几乎为一片空白,对于应用程序的验证与授权大部分的工作是开发人员自己编写代码,或者是借助企业库等工具来实现,此可谓.net1.1中的一大缺憾.在.net2.0中微软为 ...

  3. Android学习之一:Cygwin简介

    为了能够一窥Android底层的东东,还是要搭建编译Android的环境.虽有Ubuntu和Suse系统,无奈总感觉在不同的系统下切来切去很是不便.在Windows工作学习,要编译Android,就不 ...

  4. vld(Visual Leak Detector) 内存泄露检测工具

    初识Visual Leak Detector 灵活自由是C/C++语言的一大特色,而这也为C/C++程序员出了一个难题.当程序越来越复 杂时,内存的管理也会变得越加复杂,稍有不慎就会出现内存问题.内存 ...

  5. 【Java线程】volatile的适用场景

    http://www.ibm.com/developerworks/cn/java/j-jtp06197.html 把代码块声明为 synchronized,有两个重要后果,通常是指该代码具有 原子性 ...

  6. poj2531

    看了一下0ms,16ms,100ms左右过了的代码,思维量对我来说比較大,不是非常easy理解. 我的作法: 用并查集算权值和. 用dfs枚举两个点集的全部可能,因为是全然图,枚举一半的点就可以. # ...

  7. Design Pattern Chain of Reponsibility 责任链模式

    本程序实现一个责任链模式查询人名的资料. 開始都是查询第一个人,问其是否有某人的资料,假设有就返回结果,假设没有第一个人就会询问第二个人,第二个人的行为和第一个人的行为一致的,然后一致传递下去,直到找 ...

  8. C# - ref

    The ref keyword causes an argument to be passed by reference, not by value. The effect of passing by ...

  9. SPARK在linux中的部署,以及SPARK中聚类算法的使用

    眼下,SPARK在大数据处理领域十分流行.尤其是对于大规模数据集上的机器学习算法.SPARK更具有优势.一下初步介绍SPARK在linux中的部署与使用,以及当中聚类算法的实现. 在官网http:// ...

  10. js计算日期相差的天数

    在网站开发中,经常会遇到计算日期相差的天数,js 没有提供相应的方法,所以自己写一个,方便将来查看: 代码: function DateDiff(sDate1, sDate2, splitStr) { ...