java泛型实例化_java基础-泛型举例详解

泛型

泛型是JDK5.0增加的新特性,泛型的本质是参数化类型,即所操作的数据类型被指定为一个参数。这种类型参数可以在类、接口、和方法的创建中,分别被称为泛型类、泛型接口、泛型方法。

一、认识泛型

在没有泛型之前,通过对类型Object的引用来实现参数的"任意化",但"任意化"带来的缺点是需要显示的强制类型转换,此种转换要求开发者对实际参数类型预知的情况下进行,对于强制转换错误的情况,编译器可能不会提示错误,但在运行时会出现异常,这是一个安全隐患。

举例:不使用泛型实现参数化类型

1 packagegeneric;2

3 public classNoGeneric {4 private Object ob; //定义通用类型成员

5 publicNoGeneric(Object ob) {6 this.ob =ob;7 }8 publicObject getOb() {9 returnob;10 }11 public voidsetOb(Object ob) {12 this.ob =ob;13 }14 public voidshowType() {15 System.out.println("实际类型是:"+ob.getClass().getName());16 }17 }18

19 packagegeneric;20

21 public classNoGenericDemo {22

23 public static voidmain(String[] args) {24 //TODO 自动生成的方法存根25 //定义类NoGener的一个Integer版本

26 NoGeneric intob = new NoGeneric(new Integer(66));27 intob.showType();28 int i =(Integer)intob.getOb();29 System.out.println("value="+i);30 System.out.println("-----------------------------");31 //定义类NoGeneric的一个String版本

32 NoGeneric strob = new NoGeneric(new String("hello"));33 strob.showType();34 String s =(String)strob.getOb();35 System.out.println("value="+s);36 }37 }

执行结果为:

实际类型是:java.lang.Integer

value=66

-----------------------------实际类型是:java.lang.String

value=hello

上面的实例有两点需要注意:首先如下语句

String s = (String)strob.getOb();

在使用时必须明确指定返回对象需要被强制转化的类型为String,否则无法编译通过;其次,由于intob和strob都属于NoGeneric的类型,假如执行如下语句

intob = strob;

此种赋值,语法上是合法的,而在语义上是错误的,对于这种情况,只有在运行时才会出现异常,使用泛型就不会出现上述错误,泛型的好处就是在编译期 检查类型,捕捉类型不匹配错误,并且所有强制转换都是自动和隐式的,提高代码的重用率.

举例 2:使用泛型使用泛型实现参数实例化类型

packagegeneric;public class Generic{private T ob; //定义泛型成员变量

publicGeneric(T ob) {this.ob =ob;

}publicT getOb() {returnob;

}public voidsetOb(T ob) {this.ob =ob;

}public voidshowType() {

System.out.println("实例类型为:" +ob.getClass().getName());

}

}packagegeneric;public classGenericDemo {public static voidmain(String[] args) {//TODO 自动生成的方法存根//定义泛型Generic的一个Integer的版本

Generic intob = new Generic(88);

intob.showType();int i =intob.getOb();

System.out.println("value=" +i);

System.out.println("----------------------");//定义泛型Generic的一个String版本

Generic strob = new Generic("hello");

strob.showType();

String s=strob.getOb();

System.out.println("value=" +s);

}

}

运行结果为:

实例类型为:java.lang.Integer

value=88

----------------------实例类型为:java.lang.String

value=hello

在引入泛型的前提下,如果再次执行

intob = strob;

将提示错误,编译无法通过

二、泛型定义

泛型的语法可归纳为

class class-name {//......}

实例化泛型的语法为:

class-name obj = new class-name(cons-arg-list);

type-param-list用于指明当前泛型类可接受的类型参数占位符的个数;   如:

class Generic{//......}

这里的T是类型参数的名称,并且只允许传一个类型参数给Generic类,在创建对象时,T用作传递 给Generic的实际类型的占位符,每当声明类型参数时,只需用目标类型替换T即可.   如:

Generic intob;

声明对象时占位符T用于指定实际类型,如果传递实际类型为Integer,属性ob就是Integer类型,类型T还可以指定方法的返回类型     如:

publicT getOb(){returnob;

}

理解泛型有三点需要注意:

1、泛型的类型参数只能为类类型(包括自定义类),不能是基本数据类型。

2、同一种泛型可以对应多个版本(因为类型参数时不确定的)、不同版本的泛型类实例是不兼容的。

3、泛型的类型参数可以有多个。

注意   根据惯例,泛型类定义时通常使用一个唯一的大写字母表示一个类型参数.

三、有界类型

定义泛型类时,可以向类型参数指定任何类型信息,特别是集合框架操作中,可以最大限度地提高适用范围,但有时候需要对类型参数的取值进行一定程度的限制,以使数据具有可操作性.

为了处理这种情况,java提供了有界类型,.在指定类型参数时可以使用extends关键字限制此类型参数代表的类必须是继承自指定父类或父类本身.

使用extends关键字实现有界类型泛型类的定义

packagegeneric;public class BoundGeneric{//定义泛型数组

T[] array;publicBoundGeneric(T[] array) {this.array =array;

}//计算总和

public doublesum() {double sum = 0.0;for(T t : array) {

sum= sum +t.doubleValue();

}returnsum;

}

}

BoundGeneric类的定义中,使用extends将T的类型限制为Number类及其子类,故可以再定义过程中调用Number类的doubleValue方法,现在分别指定Integer,double,String类型作为类型参数,测试BoundGeneric:

packagegeneric;public classBoundGenericDemo {public static voidmain(String[] args) {//TODO 自动生成的方法存根//使用整形数组构造泛型对象

Integer[] intArray = {1, 2, 3, 4};

BoundGeneric iobj = new BoundGeneric(intArray);

System.out.println("iobj的和为:" +iobj.sum());//使用Double型数组构造泛型对象

Double[] douArray = {1.2, 2.3, 3.4, 4.5};

BoundGeneric dobj = new BoundGeneric(douArray);

System.out.println("dobj的和为:" +dobj.sum());

String[] strArray= {"str1","str2"};//下面的语句将会报错,String不是Number的子类//BoundGeneric sobj = new BoundGeneric(strArray);

}

}

运行结果为:

iobj的和为:10.0dobj的和为:11.4

注:在使用extends(如:T extends someClass)声明的泛型类进行实例化时允许传递的参数类型为:如果someClass是类,可以传递someClass本身及其子类;如果someClass接口可以传递实现接口的类

四、通配符

首先在说通配符之前先看一下这段代码:使用前面定义的Generic类.

packagegeneric;public classWildcarDemo {public static void func(Generic g) {//...

}public static voidmain(String args[]) {

Generic obj = new Generic(12);

func(obj);

Generic iobj = new Generic(12);//这里讲产生一个错误:类型 WildcarDemo 中的方法 func(Generic)对于参数(Generic)不适用//func(iobj);

}

}

上述代码的func()方法的创建意图是能够处理各种类型参数的Generic对象,因为Generic是泛型,所以在使用时需要为其指定具体的参数化类型Object,看似不成问题,

但在

func(iobj);

处产生一个编译错误,因为func定义过程中以明确声明的Generic的类型参数为Object,这里试图将Generic类型的对象传递给func()方法,类型不匹配导致编译错误.这种情况可以使用通配符解决.通配符由"?"来表示,它代表一个未知类型

packagegeneric;public classWildcarDemo2 {public static void func(Generic >g) {//...

}public static voidmain(String args[]) {

Generic obj = new Generic(12);

func(obj);

Generic iobj = new Generic(12);

func(iobj);

}

}

上述代码,在采用了通配符后语句将无误的编译,运行.

在通配符使用的过程中,也可通过extends关键字限定通配符的界定的类型参数的范围.

packagegeneric;public classWildcarDemo3 {public static void func(Generic extends Number>g) {//...

}public static voidmain(String args[]) {

Generic obj = new Generic(12);//这里将产生一个错误:类型 WildcarDemo3 中的方法 func(Generic extends Number>)对于参数(Generic)不适用//func(obj);

Generic iobj = new Generic(12);

func(iobj);

}

}

五、泛型的局限性

java并没有真正实现泛型,是编译器在编译的时候在字节码上做了手脚(称为擦除). 这种实现理念在成java泛型本身有很多漏洞, 为了避免这些问题java对泛型的使用上做了一些约束,但不可避免的还是有一些问题存在.多数的限制都是由类型擦除引起的.

1、泛型类型不能被实例化

public class Gen{

T ob;publicGen(){

ob= newT();

}

}

Gen构造器是非法的,类型擦除将变量T替换成Object,但这段代码的本意肯定不是调用new Object().类似:如

public T[]build (T[] a){

T [] array= new T[2];

//...

}

类型擦除会让这个方法总是构造一个Object[2]数组,但是可以通过调用Class.newInstance和Array.newInstance方法,利用反射构造泛型对象和数组

2、数组

不能实例化数组如:

T[] vals;

vals= new T[10];

因为T在运行时时不存在的,编译器无法知道实际创建那种类型的数据.

其次,不能创建一个类型特定的泛型引用的数组    如:

Gen []arrays = new Gen[100];

上面的代码会损害类型安全

如果使用通配符,就可以创建泛型类型的引用数组

Gen> []arrays = new Gen>[10];

3、怒能用类型参数替换基本类型

因为擦除类型后原先的类型参数被Object或者限定类型替换,而基本类型是不能被对象所存储的,可以使用基本类型的包装类来解决此问题

4、异常

不能抛出也不能捕获泛型类的异常对象,使用泛型类来扩展Throwable也是非法的. 如:

public class GenericException extendsException{//泛型类无法继承Throwable

}

不能再catch子句中使用类型参数,例如下面的方法将不能编译

public static void doWork(Classt) {try{//...

}catch(Throwable realCause) {//...

}

但是在异常声明时可以使用类型参数,如:

public static void doWork(T t) throwsT {try{//...

}catch(Throwable realCause) {throwt;

}

}

5、静态成员

不能在静态变量或者静态方法中引用类型参数  如:

public class Gen{staticT ob;staticT getOb() {returnob;

}

}

这些均参考自“Java SE程序设计”,算是做个笔记,以后忘了可以翻阅一下,写在自己的随笔中,也希望可以帮助更多的人。如有侵权,请联系本人删除

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值