您的位置:首页 > 其它

泛型(一)

2016-04-14 00:00 148 查看
摘要: 梳理一下泛型,希望可以加深对理解。

  java1.5发行版中增加了泛型,用泛型可以避免对象的强制转换,这就是我最泛型最初的了解,还有那时用的最多的地方也就是集合类了。泛型到底是什么鬼?今天来梳理一下,希望可以加深对泛型的理解。

一、几个术语

泛型类、接口:声明中具有一个或多个类型参数的类或者接口。

泛型:泛型类和接口统称为泛型(generic type)。

  怎么样,是不是很拗口。简单点来说就是如果一个类或者一个接口的声明中使用了类型参数,那这个类或接口就是泛型类、接口,他们统称为泛型。可以发现,关键之处在于是否用了类型参数,那类型参数又是什么鬼?当然就是指代表某一个类型的参数,可以实际类型参数(比如<String>),也可以形式类型参数(比如)。好啦不管你明白还是没有明白,先不要纠结于概念。
二、尽量使用泛型

  每种泛型定义的是一组参数化的类型,比如List就是一种参数化的类型,表示元素类型为字符串的列表。
  在开发的过程中尽可能的使用泛型,泛型可以保证类型的安全,使用泛型可以在编译期就避免类型的不安全因素,同时泛型还可以避免强制类型转换。
  比如原生类型List和泛型List,这两者之间是有区别的,List实际上直接逃避了类型检查,而List明确告诉编译器,它能持有任意类型的对象。比如你可以将List传递给List,但是不能将List传递给List。所以如果使用List这种原生类型就会丢掉线程安全性。看下面的例子吧。  
public class GenericTest {

/**
* 这里的List参数是类型不安全的,List直接逃避了类型检查
* 比如当传参为List<String>或者任何一种类型的List,这个方法都可以接收,不会报错
* 因为List根本不会去检查类型(直接逃避了类型检查),但这样做事类型不安全的
* @param list
* @param o
*/
public static void addObj1(List list,Object o){
list.add(o);
}

/**
* 参数化类型List<Object>是类型安全的,表示它可以接收所有类型的对象
* 如果调用该方法时传一个List<String>参数进去,是会报错的,因为该方法的参数是一个可以接收任何类型对象的List,
* 而List<String>只是一个String类型的List
* @param list
* @param o
*/
public static void addObj2(List<Object> list,Object o){
 
3ff0
;    list.add(o);
}

public static void addObj3(List<String> list,String s){
list.add(s);
}

public static void main(String[] args) {
List<String> stringList = new ArrayList<String>();

/**
* 这里放List<String>里面放了一个Interger类型的对象,
* 但是编译时没有报错,但是,运行时是会报错的,所以是类型不安全的
*/
addObj1(stringList, new Integer(18));
/**
* 这里编译时会报错
* The method addObj2(List<Object>, Object) in the type GenericTest is not applicable for the arguments (List<String>, Integer)
*/
addObj2(stringList, new Integer(18));

/**
* 这才是网参数类型列表List<String>增加对象的正确方式
*/
addObj3(stringList, new Integer(18).toString());

}

}

  有两种例外情况不能使用泛型(会报错),一个是类文字不允许使用参数化类型,比如List和List.class都不合法,另一种情况是在参数化类型而非无限制通配符类型上使用instanceof是不可以的。

三、使用通配符

  我们说尽可能的使用泛型(参数化的类型),但是当你确定或者不关心实际参数的类型时,可以使用通配符?代替,比如Set<?>(读作某个类型的集合)。问号在这里表示无限制的通配符类型
  使用无限制的通配符类型有个问题,不能将任何元素(除null之外),放到Collection<?>中,如果向下面这样做的话,编译时就会报错。
public static void testUnsafeAdd(Set<?> set,Object o){
set.add(new Object());//这里是编译不过的,因为不能将任何元素(除null之外),放到Collection<?>之中
}


报错如下:
The method add(capture#1-of ?) in the type Set is not applicable for the arguments (Object)

  显然上面这种无限制的通配符类型的使用方式并不能满足我们的需要(无法往集合添加除null之外的任何元素),所以我们就应该使用泛型方法或者有限制的通配符类型(比如Set<? extends E>)。

四、消除所有非受检警告

  当我们使用强制类型转换或者通过非泛型的方式使用结合等等,这些可能存在类型不安全的情况时,编译期就会报非受检警告(是警告,编译的时候不会报错,但是运行时如果存在类型不安全的情况时就会报错)。
  当我们越熟悉泛型,泛型使用的越多的时候,非受检警告就会越少,但是有时候还是会有一些警告无法消除,这时候如果你可以证明引起警告的代码是类型安全的,那么就可以用一个@SuppressWarnings(“unchecked”)注解在禁止这条警告。并且最好在写一个注释,标明它为什么是类型安全的。
  有一个最佳实践就是,我们应该在最小的范围中使用SuppressWarnings注解,通常是某个变量的声明或者某个简短的方法或构造器。永远不要再整个类上使用@SuppressWarnings。

五、创建泛型数组是非法的

  数组和泛型不能配合使用,因为数组是协变的,比如若Sub是Super的子类型,那么sub[]就是Super[]的子类型。相反。泛型是不可变的
  数组与泛型的另一个区别就是,数组是具体化的,即数组会在运行的时候检查元素的类型约束,而泛型在编译的时候检查类型信息,并在运行的时候擦除元素的类型信息。
public static void testArray(Object[] objArray,Object obj){
objArray[0] = obj;
}

Long[] longArray = new Long[10];
testArray(longArray, "hahaha");//这里运行的时候才会去检查,网Long数组里放了一个String


泛型是通过擦除实现的,比如List实例运行时类型只是List。 创建无限制通配符类型(?)的数组是合法的,但是不常用。 当得到泛型数组创建错误的时候,最好的解决办法通常就是使用泛型集合List,而不是数组类型E[].

六、优先使用泛型

  当我们设计一个新的类型时候,尽量做成泛型的,这样可以避免类型转换,类型会更加安全。会面会继续探索泛型类、泛型方法,以及如果通过泛型提升API的灵活性。

注:参阅《Effect Java》
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: