深入理解Java泛型

来源:互联网 发布:金蝶软件北京分公司 编辑:程序博客网 时间:2024/05/17 04:49

泛型引入

Java是一种强类型的语言,定义一个变量时需要指明其类型。Object是所有类的基类,在Java 1.5之前,为了让类具有通用性,通常使用Object来实现参数的任意化,如将String、Double等存储为Object类型,这个过程叫做自动装箱或向上转型。但是问题在于取数据时,必须做强制类型转换,将Object向下转型为String或Double等类型。向下转型存在很大的风险,需要事先知道具体的下转型类型是什么,一旦忘记或写错,在运行必然抛出异常,但在编译期间却不易发现,因此存在极大的安全隐患,应该尽量避免使用向下转型。

下面是使用Object的一个示例:

public class ObjectFoo {    private Object foo;    public ObjectFoo(Object f) {        this.foo = f;    }    public Object getFoo() {        return foo;    }    public void setFoo(Object foo) {        this.foo = foo;    }}

具体调用如下:

ObjectFoo strFoo1 = new ObjectFoo("hello Object");ObjectFoo intFoo1 = new ObjectFoo(100);// 此处需要强制类型转换为StringString strRst = (String) strFoo1.getFoo();// 此处需要强制类型转换为intint intRst = (int) intFoo1.getFoo();System.out.println("strFoo1.getFoo:" + strRst);System.out.println("intFoo1.getFoo:" + intRst);

正因为这个过程麻烦且容易出错,在Java 1.5中推出了泛型这个新特性,其本质是参数化类型。泛型在使用时会指明具体的类型,因此不需要向下转型,编译的时候也会检查类型安全,泛型还可以提高代码重用性,概括来说就是简单易用并且安全,泛型可用在类、接口、方法中,分别叫做泛型类、泛型接口和泛型方法,下面会一一介绍。

泛型类

依然以上述场景为例,先看一下利用泛型类是如何实现的:

public class GenericsFoo<T> {    private T foo;    public GenericsFoo(T f) {        this.foo = f;    }    public T getFoo() {        return foo;    }    public void setFoo(T foo) {        this.foo = foo;    }}

泛型类的调用:

// 使用泛型时必须指定具体类型,如String,不再需要类型转换GenericsFoo<String> strFoo2 = new GenericsFoo<>("Hello Generics");// 使用泛型时必须指定具体类型,如Integer,不再需要类型转换GenericsFoo<Integer> intFoo2 = new GenericsFoo<>(100);System.out.println("strFoo2.getFoo:" + strFoo2.getFoo());System.out.println("intFoo2.getFoo:" + intFoo2.getFoo());

从上面代码可以看到,跟普通的类相比,多出了<T>符号,T是自定义的标识符,也作为参数,用来传递数据类型,称之为类型参数。T可以理解为数据类型的占位符,在运行时会被替换为真正的数据类型,上面示例中T会被替换为String和Integer。

泛型规则

  1. 泛型类型参数只能是类类型,包括自定义类。
  2. 类型参数不一定写为T,实际上可以任意定义。但习惯上使用单个大写字母,并且通常有如下含义:T-Type(表示一般数据类型)、K-Key(表示键)、V-Value(表示值)、N-Number(表示数值类型)、E-Element(集合中的元素,在集合中使用)、?(表示不确定的类型,用作通配符)等等。
  3. 泛型的类型参数可以有多个,用逗号隔开,如<K, V>
  4. 泛型的参数类型可以使用extends语句,例如<T extends superclass>,叫做限制泛型。如果有多个类和接口,写法为<T extends SomeClass & interface1 & interface2 & interface3>,但只能存在一个类,因为Java只能继承一个类,多个接口需写在类的后面。
  5. 泛型的参数类型可以是通配符类型,例如Class<?> classType = Class.forName("java.lang.String")

泛型方法

使用泛型方法时不必指明参数类型,编译器会根据传递的参数自动查找具体的类型,这一点与泛型类不同。泛型方法与其所在类是不是泛型类没有关系,普通类也可拥有泛型方法,要定义一个泛型方法,只需要将类型参数放在修饰符之后、返回值之前即可。一旦定义了类型参数,就可以在参数列表、方法体及返回值中使用了。

下面代码是在上述泛型类的基础上增加了一个泛型方法:

public class GenericsFoo<T> {    private T foo;    public GenericsFoo(T f) {        this.foo = f;    }    public T getFoo() {        return foo;    }    public void setFoo(T foo) {        this.foo = foo;    }    // 泛型方法    public <V> void getValue(V f) {        System.out.println("The value is:" + f);    }}

泛型方法除了定义不同,调用方法就跟普通方法一样:

GenericsFoo<String> strFoo2 = new GenericsFoo<>("Hello");strFoo2.getValue(strFoo2.getFoo());strFoo2.getValue("Generics Method");strFoo2.getValue(255);

输出结果:

The value is:Hello
The value is:Generics Method
The value is:255

泛型接口

泛型接口跟泛型类定义很像,直接放上示例代码,相信大家都能看懂。

定义泛型接口:

public interface Info<T> {    public T getVar();}

泛型接口的实现类:

public class InfoImp<T> implements Info<T> {    private T var;    public InfoImp(T var) {        this.var = var;    }    public void setVar(T var) {        this.var = var;    }    @Override    public T getVar() {        return var;    }}

调用示例:

public class GenericsInterface {    public static void main(String[] args) {        Info<String> strObj = new InfoImp<>("Hello Generics Interface");        System.out.println("The string value is:" + strObj.getVar());        Info<Integer> intObj = new InfoImp<>(1024);        System.out.println("The integer value is:" + intObj.getVar());    }}

输出结果:

The string value is:Hello Generics Interface
The integer value is:1024

限制泛型

上面泛型类的示例中public class GenericsFoo<T> 并没有限制类型参数T的类型,实际上这里相当于Object,可以是任何类型。有时候我们需要对此处的类型做特定限制,这就是限制泛型了,听起来有点抽象,举个例子很容易就明白了。

限制泛型的基本语法为<T extends SuperClass(或Interface)>,这里的extends需要广义理解,接口也是用它,这就表示T的类型只能是该SuperClass的子类或者是任何实现了这个接口的类的类型,这样就对泛型做了限制。

假设定义一个父类Geometry:

public class Geometry {    public String name;    public double area;}

再定义3个类,其中Circle、Square继承自Geometry,Keyboard没有继承Geometry。

public class Circle extends Geometry {}
public class Square extends Geometry {}
public class Keyboard {}

限制泛型类的定义如下:

public class GenericsRestrict<T extends Geometry> {}

如上所示,泛型类型仅能是Geomerty或其子类,其他类型则会报错。

调用测试,如果是限制之外的类型就会报错:

public class TestClass {    public static void main(String[] args) {        GenericsRestrict<Square> square = new GenericsRestrict<>();        GenericsRestrict<Circle> circle = new GenericsRestrict<>();        // 因为Keyboard没有继承Geometry,下面语句报错        GenericsRestrict<Keyboard> keyboard = new GenericsRestrict<>();    }}

当然extends后面可以是接口或类与多个接口,规范及原理相同,这里不再赘述。

通配符泛型

为了解决类型被限制死了而不能动态根据需要来确定的缺点,引入了“通配符泛型”, 如<? extends Collection>,?代表未知类型,这个类型可以是Collection接口的所有实现类。使用通配符有以下两点需要注意:

  1. 如果只指定了<?>,而没有extends,则可以是任意类型。
  2. 使用通配符?不但可以限制类型的上限,还可以限制下限。限制下限使用 super 关键字,例如 <? super Number> 表示只能接受 Number 及其父类,可接受的最低类型为Number。上限限制则是使用上面提到的extends关键字。

注意

如果在使用泛型时没有指定具体的数据类型,就会擦除泛型类型,并向上转型为 Object,在获取数据时必须向下强制类型转换,这与不使用泛型是一样的。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 京东买的暴风电视出现问题了怎么办 控水一个月的三角梅还没开花怎么办 帮别人办手机分期不还怎么办 国美在线没有信用卡分期不了怎么办 手机店办理分期被老板套现了怎么办 美的空调保修卡丢了怎么办 格力空调保修卡丢了怎么办 荣耀9i手机总是滑手怎么办? 春兰空调没发票不给修怎么办 洗衣机顶盖的安全开关坏了怎么办 苹果手机的开关健坏了怎么办 淘宝买的东西快递弄破损了怎么办 京东购买邮来手机里面没有怎么办 孕期建卡病历本丢了怎么办 四维检查胎儿心脏有缺陷怎么办 七个月的宝宝俩个蛋蛋都疝气怎么办 电脑有些网站看视频不能全屏怎么办 小米滑板车坏了售后不保修怎么办 红米4a电池不耐用怎么办 华为手机买个别人退货的怎么办 京东购物怎么查订单查询不到怎么办 退款了又收到货怎么办快递打电话 快递未收到货能退款商家拒绝怎么办 黑色牛仔裤有一块洗白了怎么办 黑色牛仔裤被洗衣液烧了怎么办 在蘑菇街退鞋子商家非说脏了怎么办 牛皮屑怎么办ke靠成都银康 微信订阅号取消关注之后还在怎么办 腾讯新闻红包领取说帐号异常怎么办 计算机职弥报名没选模块怎么办 有人用我手机注册有赞了怎么办 一件代发别人的货被投诉了怎么办 淘宝极速退款后商家拒收快递怎么办 运费险退到天猫垫付账户了怎么办 淘宝卖家食品有问题该怎么办 美团外卖不要辣椒给放了怎么办 旺旺卖家拒收我的消息怎么办 淘宝清空购物车大奖到上限了怎么办 游戏无响应除了退出还能怎么办 淘宝给差评了卖家一直打电话怎么办 电脑说带宽问题无法观看视频怎么办