天天看点

Java 泛型详解

在日常的开发中,我们会看到别人的框架很多地方会使用到泛型,泛型是java se 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。本篇博客我们就来详细解析一下泛型的知识。

泛型类定义及使用

使用泛型有什么好处呢?首先我们先看一个例子,假设我们有两个类,代码如下:

#stringclass 

public class stringclass {

    private string x ;

    private string y ;

    public string gety() {

        return y;

    }

    public void sety(string y) {

        this.y = y;

    public string getx() {

        return x;

    public void setx(string x) {

        this.x = x;

}

#intclass 

public class intclass {

    private int x ;

    private int y ;

    public int gety() {

    public void sety(int y) {

    public int getx() {

    public void setx(int x) {

观察上面两个类stringclass 和intclass,他们除了变量类型不一样,一个是string一个是int以外,其它并没有什么区别!那我们能不能合并成一个呢?通过泛型就可以解决,首先看一下泛型的类是怎么定义的:

public class objclass<t> {

    private t x ;

    private t y ;

    public t getx() {

    public void setx(t x) {

    public t gety() {

    public void sety(t y) {

那么这时候上面的两个类就可以通过泛型的设置,相应生成

objclass<string> stringclass = new objclass<string>();

        stringclass.setx("haha");

        objclass<integer> intclass = new objclass<integer>();

        intclass.setx(100);

        log.d("yyy", "stringclass:" + stringclass.getx() + ",intclass:" + intclass.getx());

从结果中可以看到,我们通过泛型实现了开篇中stringclass类和intclass类的效果。

接下来介绍泛型如何定义及使用:

1.首先需要定义泛型:objclass

objclass ,即在类名后面加一个尖括号,括号里是一个大写字母。这里写的是t,其实这个字母可以是任何大写字母,无论使用哪个字母,意义都是相同的。如果你想学习java可以来这个群,首先是二二零,中间是一四二,最后是九零六,里面有大量的学习资料可以下载

2.在类中使用泛型

这个t表示派生自object类的任何类,比如string,integer,double等等。这里要注意的是,t一定是派生于object类的。

private t x ;

3.使用泛型类

泛型类的使用代码如下:

首先,需要构造一个实例:

泛型类的构造则需要在类名后添加上,即一对尖括号,中间写上要传入的类型。

因为我们构造时,是这样的:objclass,所以在使用的时候也要在objclass后加上类型来定义t代表的意义。

尖括号中,你传进去的是什么,t就代表什么类型。这就是泛型的最大作用,我们只需要考虑逻辑实现,就能拿给各种类来用。

多泛型变量定义

1.多泛型变量定义

我们不止可以在类中设置一个泛型变量t,还可以声明多个泛型变量,写法如下:

public class objclass<t,u>

也就是在原来的t后面用逗号隔开,写上其它的任意大写字母即可,如果还有多个,依然使用逗号分隔开即可,则我们前面定义的泛型类就会变成下面这样:

public class objclass<t,u> {

    private u y ;

    public u gety() {

    public void sety(u y) {

objclass<string,integer> stringclass = new objclass<string,integer>();

        stringclass.sety(100);

从上面的代码中,可以明显看出,就是在新添加的泛型变量u用法与t是一样的。

2.泛型的字母规范

虽然在类中声明泛型任意字母都可以,但为了可读性,最好遵循以下的规范:

e — element,常用在java collection里,如:  list<e>,iterator<e>,set<e>

 k,v — key,value,代表map的键值对

 n — number,数字

 t — type,类型,如string,integer等等

泛型接口定义及使用

在接口上定义泛型与在类中定义泛型是一样的,代码如下:

interface msgclass<t> {

    public t getmsg() ;

    public void setmsg(t x);

我们可以利用泛型类来构造填充泛型接口

public class message<t,u> implements msgclass<t>{

    private t msg;

    @override

    public t getmsg() {

        return msg;

    public void setmsg(t msg) {

        this.msg = msg;

在这个类中,我们构造了一个泛型类message,然后把泛型变量t传给了msgclass,这说明接口和泛型类使用的都是同一个泛型变量。

我们还可以构造一个多个泛型变量的类,并继承自msgclass接口:

    private u name;

    public u getname() {

        return name;

    public void setname(u name) {

        this.name = name;

泛型函数定义及使用

我们不但可以在类声明中使用泛型,还可以在函数声明中也使用泛型,使用如下:

public class objclass {

    //静态函数

    public static <t> void staticmethod(t a) {

    //普通函数

    public <t> void orgnicmethod(t a) {

上面分别是静态泛型函数和常规泛型函数的定义方法,与以往方法的唯一不同点就是在返回值前加上来表示泛型变量。

无论哪种泛型方法都有两种使用方法:

//静态方法

objclass.staticmethod("adfdsa");//使用方法一

objclass.<string>staticmethod("adfdsa");//使用方法二

//常规方法

objclass objclass = new objclass();

objclass.orgnicmethod(new integer(111));//使用方法一

objclass.<integer>orgnicmethod(new integer(111));//使用方法二

方法一,隐式传递了t的类型,这种隐式的传递方式,代码不利于阅读和维护。因为从外观根本看不出来你调用的是一个泛型函数。

方法二,例如上面例子中,将t赋值为integer类型,这样orgnicmethod(t a)传递过来的参数如果不是integer那么编译器就会报错。

当然泛型函数的返回值也可以使用泛型表示:

public static <t> list<t> parsearray(string response,class<t> object){  

    list<t> modellist = json.parsearray(response, object);  

    return modellist;  

函数返回值是list类型。和void的泛型函数不同,有返回值的泛型函数要在函数定义的中在返回值前加上标识泛型;还要说明的是,上面中,使用class传递泛型类class对象

泛型数组

泛型同样可以用来定义在数组上

//定义  

        public static <t> t[] fun1(t...msg){  // 接收可变参数    

            return msg ;            // 返回泛型数组    

        }

        //使用  

        public static void main(string args[]){

            integer i[] = fun1(8,9,8,44) ;

            integer[] result = fun1(i) ;

定义了一个静态函数,然后定义返回值为t[],参数为接收的t类型的可变长参数。

泛型的通配符

在开发中对象的引用传递(向上向下传递)是最常见的,但是,在泛型的操作中,在进行引用传递的时候泛型类型必须匹配才可以传递,否则不能传递。

例如,如下没有进行泛型类型匹配,一个是string,一个是object类型。

class info<t>{

    private t var ;        // 定义泛型变量

    public void setvar(t var){

        this.var = var ;

    public t getvar(){

        return this.var ;

    public string tostring(){   

        return this.var.tostring() ;

};

public class demo1 {

        public static void main(string args[]) {

            // 使用string为泛型类型

            info<string> i = new info<string>();        

            i.setvar("abcd");

            //把string泛型类型的i对象传递给object泛型类型的temp。

            fun(i);                   

        // 接收object泛型类型的info对象

        public static void fun(info<object> temp) {        

            system.out.println("内容:" + temp);

编译发生错误。

exception in thread "main" java.lang.error: unresolved compilation problem: 

    the method fun(info<object>) in the type demo1 is not applicable for the arguments (info<string>)

    at thread1.demo1.main(demo1.java:18)

泛型对象进行引用传递的时候,类型必须一致,如果非要传递,则可以将fun方法中info参数的泛型取消掉(变成 void fun(info temp))。、

以上确实改进了功能,但是似乎不是很妥当,毕竟之前指定过泛型。

以上程序在fun()方法中使用 "info<?>" 的代码形式,表示可以使用任意的泛型类型对象,这样的话fun()方法定义就合理了,但是使用以上方法也有需要注意的地方,

即:如果使用“?“接收泛型对象的时候,则不能设置被泛型指定的内容。

    private t var ;        

    public string tostring(){    

public class genericsdemo{

    public static void main(string args[]){

        info<string> i = new info<string>() ;       

        i.setvar("abcd") ;                            

        fun(i) ;

    public static void fun(info<?> temp){        

        system.out.println("内容:" + temp) ;

如果使用”?“意味着可以接收任意的内容,但是此内容无法直接使得用”?“修饰的泛型的对象进行修改。如下就会出问题:

public class demo1{

        info<?> i = new info<string>() ;       

运行结果:

    the method setvar(capture#1-of ?) in the type info<capture#1-of ?> is not applicable for the arguments (string)

    at thread1.demo1.main(demo1.java:17)

在使用”?“只能接收,不能修改。

泛型的上限

        info<integer> i1 = new info<integer>() ;        

        info<float> i2 = new info<float>() ;            

        i1.setvar(30) ;                                    

        i2.setvar(30.1f) ;                                

        fun(i1) ;

        fun(i2) ;

    public static void fun(info<? extends number> temp){    // 只能接收number及其number的子类

        system.out.print(temp + "、") ;

运行成功。但是,如果传入的泛型类型为string的话就不行,因为string不是number子类。

在类中使用泛型上限。

class info<t extends number>{    // 此处泛型只能是数字类型

        info<integer> i1 = new info<integer>() ;        // 声明integer的泛型对象

如果在使用info的时候设置成string类型,则编译的时候将会出现错误(string不是number子类)

注意:利用 <? extends number> 定义的变量,只可取其中的值,不可修改

原因如下:

因为info的类型为 info

泛型的下限

<? super xxx> 表示填充为任意xxx的父类

public class genericsdemo21{

        info<string> i1 = new info<string>() ;        // 

        info<object> i2 = new info<object>() ;        // 

        i1.setvar("hello") ;

        i2.setvar(new object()) ;

    public static void fun(info<? super string> temp){    // 只能接收string或object类型的泛型,string类的父类只有object类

object类和string类都是string的父类,所有运行成功,但是如果此时用integer则会出错,因为integer并不是string父类。

注意:使用super通配符:能存不能取

如何理解呢?假设有3个类,继承关系如下:

class ceo extends manager {  

}  

class manager extends employee {  

class employee {  

然后书写如下代码:

list<? super manager> list;  

list = new arraylist<employee>();  

//存  

list.add(new employee()); //编译错误  

list.add(new manager());  

list.add(new ceo());

为什么而list.add(new employee());是错误的?

因为list里item的类型是

list<employee> list = new arraylist<employee>();  

在这里,正因为manager和ceo都是employee的子类,在传进去list.add()后,会被强制转换为employee!

现在回过头来看这个:

编译器无法确定 <? super manager> 的具体类型,但唯一可以确定的是manager()、ceo()肯定是 <? super manager> 的子类,所以肯定是可以add进去的。但employee不一定是 <? super manager> 的子类,所以不能确定,不能确定的,肯定是不允许的,所以会报编译错误。

最后强调一下, list<? super manager> list取出的只能是object 类型,这里虽然看起来是能取的,但取出来一个object类型,是毫无意义的。所以才有了“super通配符:能存不能取”的结论。

总结1)使用?可以接收任意泛型对象。

2)泛型的上限:?extends 类型(能取不能存)。

3)泛型的下限:?super 类型? super 通配符(能存不能取)。