黑马程序员——java中的泛型

 

 

---------------------- <a href="http://edu.csdn.net"target="blank">ASP.Net+Android+IOS开发</a>、<a href="http://edu.csdn.net"target="blank">.Net培训</a>、期待与您交流! ----------------------

1.      什么是泛型:

泛型本质上是提供类型的"类型参数",也被称为参数化类型(parameterizedtype)或参量多态(parametric polymorphism)。

泛型是JDK 1.5的新特性,可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。

2.      泛型的好处:

泛型将运行时期可能产生的问题(ClassCastException)转移到了编译期,这样在编译时期,程序员就可以发现和解决问题。

JDK 1.5之前是通过对类型Object的引用来实现参数的“任意化”,这样做的缺点就是要进行显式的强制类型转换。而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。并且对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。

3.       泛型在集合中的应用:

JDK1.5之后,java中集合的定义均有泛型的存在。以ArrayList为例,在定义时采用了ArrayList<E> 形式,其中的 <E> 就是泛型。当在程序中具体应用时,可把E用具体的类型代替(String),则这时集合ArrayList<String>中只能存储String类型的对象。

泛型在集合中的应用代码示例如下,之后会对其中存在的问题进行分析:

public classArrayListDemo {

 

       public static void main(String[] args)throws Exception {

              ArrayList<String> al = newArrayList<String>();

              //ArrayList<String> al1 =new ArrayList<Integer>();//该行编译失败;

              //ArrayList<String> al2 =new ArrayList(); //该行有警告,但编译可通过;

              al.add("abc");

              //al.add(Integer(1));  //该行编译失败;

              al.add("zhangsan");

              System.out.println(al);

              //下面通过反射的方式向al中添加非String类型的对象;

              MethodmAdd=al.getClass().getMethod("add",Object.class);

              mAdd.invoke(al, 1);

              System.out.println(al);

       }

}

对上述代码分析如下:

a)      当集合被限定存储类型后,集合中的元素只能是被限定的类型,若添加了其它类型的对象,则编译失败。

b)      若创建集合对象时,等号两端都有泛型,则泛型类型必须相同,否则编译出错;

c)      可允许等号两端只有一端存在泛型的情况;但会有警告;

d)      泛型是给编译器看到,编译后会进行去泛型(泛型被擦除);所以集合中若源码限定了具体类型,在运行时通过反射可向集合中添加其他类型;

4.       自定义泛型方法:

假设要定义一个方法用于打印集合中元素,但是该集合中元素类型事先并不知道,这时若采用泛型的方式,则代码如下:

importjava.util.*;

 

public classGenericShow {

 

       public static void main(String[] args) {

              //创建一个元素类型为StringArrayList集合,调用show()

              List<String> al=newArrayList<String>();

              al.add("a");

              al.add("zhangsan");

              show(al);

              //创建一个元素类型为IntegerArrayList集合,调用show()

              List<Integer> al1=newArrayList<Integer>();

              al1.add(1);

              al1.add(10);

              show(al1);

              //创建一个元素类型为CharacterTreeSet集合,调用show()

              Set<Character> ts=newTreeSet<Character>();

              ts.add('b');

              ts.add('z');

              show(ts);

       }

       public static <T> voidshow(Collection<T> c){

              for (T t : c){

                     System.out.print(t);

                     System.out.print('\t');

              }

              System.out.println();

       }

}

由此可见,泛型的使用可以提高程序的扩展性;

5.       泛型方法实际参数类型的推断:

编译器判断范型方法的实际类型参数的过程称为类型推断,类型推断是相对于知觉推断的,其实现方法是一种非常复杂的过程。

根据调用泛型方法时实际传递的参数类型或返回值的类型来推断,具体规则如下:

a)      当某个类型变量只在整个参数列表中的所有参数或返回值中的一处被应用了,那么根据调用方法时该处的实际应用类型来确定,即直接根据调用方法时传递的参数类型或返回值来决定泛型参数的类型,例如:

swap(new String[3], 3, 4) -->    static <E> voidswap(E[] a, int i, int j)

       则泛型类型为String

b)      当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型都对应同一种类型来确定,则该类型就是相应的泛型类型,例如:

add(3, 5)  -->  static <T> T add(T a, T b)

       则泛型类型为Integer

c)      当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,且没有存在返回值,这时候取多个参数中的最小并集类型,例如,下面语句实际对应的类型就是Number了,编译没问题,只是运行时出问题:

fill(new Integer[3], 3.5f) -->  static <T> voidfill(T[] a, T v)

d)      当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,并且存在返回值,这时候优先考虑返回值的类型,例如,下面语句实际对应的类型就是Integer

int x =(3, 3.5f) -->  static <T> T add(Ta, T b)

e)      参数类型的类型推断具有传递性,下面第一种情况推断实际参数类型为Object,编译没有问题,而第二种情况则根据参数化的Vector类实例将类型变量直接确定为String类型,编译将出现问题:

copy(new Integer[5], new String[5]) --> static<T> void copy(T[] a, T[] b);

copy(new Vector<String>(), new Integer[5])--> static <T> void copy(Collection<T> a , T[] b);

6.       泛型中的通配符:

当具体类型不确定的时候,可以使用泛型通配符就是 <?>,又叫占位符;当操作类型时,若不需要使用类型的具体功能,而只使用Object类中的功能这时就可以用通配符来表未知类型。

泛型限定:

上限:?extends E:可以接收E类型或者E的子类型对象。

下限:?super E:可以接收E类型或者E的父类型对象。

泛型限定在泛型方法中的应用,以?extends E 为例,代码如下:

importjava.util.*;

 

public classGeneric_extends {

 

       public static void main(String[] args) {

              //创建一个元素类型为StringArrayList集合,调用show()

              List<String> al=newArrayList<String>();

              al.add("a");

              al.add("zhangsan");

              show(al);

              //创建一个元素类型为CharacterTreeSet集合,调用show()

              Set<Character> ts=newTreeSet<Character>();

              ts.add('b');

              ts.add('z');

              show(ts);  // 由于show()进行了泛型限定,只能接受List的子类,所以编译出错;

       }

       public static void show(List<?> c){

                     System.out.println(c.size());

       }

}

7.       自定义泛型类:

如果类的实例对象中的多处都要用到同一泛型参数,即这些地方引用的泛型类型要保持同一个实际类型时,采用泛型类型的方式进行定义,这就是类级别的泛型。

定义泛型类的简易代码如下:

classGeneric_Class<T>{

       //在泛型类中定义非静态的方法;

       public void show(T t){

              System.out.println(t);

       }

       //在泛型类中定义静态方法;

       public static <T> void show1(T t){

              System.out.println(t);

       }

       //静态方法的泛型只能定义在方法上,不能使用定义在类上的泛型;

       public static void show2(T t){

              System.out.println(t);

       }

}

从上述代码可以看出,静态方法的泛型只能定义在方法上,不能使用定义在类上的泛型;这是由于定义在类上的泛型只有在创建对象时才能确定具体的类型,而静态方法的存在优先于对象,所以静态方法的泛型只能定义在方法上。

 

 

---------------------- <a href="http://edu.csdn.net"target="blank">ASP.Net+Android+IOS开发</a>、<a href="http://edu.csdn.net"target="blank">.Net培训</a>、期待与您交流! ----------------------

详细请查看:<a href="http://edu.csdn.net" target="blank">http://edu.csdn.net</a>

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值