您的位置:首页 > 编程语言 > Java开发

Java泛型总结

2016-04-10 19:39 417 查看
所谓泛型,就是允许在定义类,接口,方法式使用类型形参,这个类型形参将在声明变量,创建对象,调用方法时动态地指定。

可以为任何类,接口增加泛型声明。例如:

public class Apple<T>{
private T info;
}


当使用Apple类时就可以为T类型形参传入实际类型。

当创建了带泛型声明的接口,父类之后,可以为该接口创建实现类,或者从此父类派生子类。但是当使用这些接口,父类是不能再包含类型形参的。例如,下面的代码是错误的:

public class A extends Apple<T>{
}


上面的代码应该写成

public class A1 extends Apple<String>{
}


不存在泛型类

List<String> l1 = new ArrayList<>();
List<Integer> l2 = new ArrayList<>();
System.out.println(l1.getClass()==l2.getClass())//true


正是因为对于Java来说,不管为泛型的类型形参传入哪一种类型实参,在内存中只占用一块内存空间,因此在静态方法、静态初始化块或者静态变量的声明和初始化中都不允许来使用类型形参,例如:

//wrong
class R<T>{
static T info;
public static void bar(T msg){}
}


而且在系统中并不会真正存在泛型类,所以instanceof运算符后面不能使用泛型类。例如下面的代码是错误的:

Collection<String> cs=new ArrayList<String>();
if(cs instanceof ArrayList<String>){...}


如果Foo是Bar的是一个子类型(子类或者子接口),而G是具有泛型声明的类或者接口。
G<Foo>
并不是
G<Bar>
的子类型,
下面的代码会引发ArrayStoreException异常:

public static void  test(List<Object> c){
for (int i = 0; i < c.size(); i++) {
System.out.println(c.get(i));
}
}
List<String> strList=new ArrayList<>();
test(strList);


为了表示各种泛型List的父类,可以使用类型通配符?

但是这种通配符的List仅能表示它是各种泛型List的父类,并不能把元素加入到其中。例如,下面的代码会导致编译错误,因为add方法需要传入的参数应该是E类的对象或者其子类的对象,但是此时程序无法确定c集合中的元素,所以不能向其中添加对象。

List<?> c=new ArrayList<String>();
c.add(new Object());


设定类型通配符的上限

若Circle类和Rectangle类继承Shape类,那么
List<? extends Shape>
可以表示为
List<Circle>
List<Rectangle>
的父类。此时Shape就成为这个通配符的上限

类似的,由于程序无法确定这个受限制的通配符的具体类型,所以不能把Shape对象或其子类的对象加入到这个泛型集合中。例如下面的代码是错误的:

public void addRectangle(List<? extends Shape> shapes){
shapes.add(0, new Rectangle());
}


同时可以设置类型形参的上限,例如下面代码就指明T类型必须是Number类或其子类,并必须实现Serializable接口:

public class Apple<T extends Number & Serializable>{
}


设定类型通配符下限

List<? super T>


泛型方法的语法如下:

修饰符 <T ,S> 返回值类型  方法名(形参列表){
//方法体
}


泛型方法的方法签名比普通方法的方法签名多了类型形参声明,类型形参声明以尖括号括起来,所有的类型形参声明放在方法修饰符和方法返回值之间。例如:

static <T> void fromArrayToCollection(T[] a,Collection<T> c){
for(T o:a){
c.add(o);
}
}

String[] sa=new String[100];
Collection<Object> co=new ArrayList<Object>();
fromArrayToCollection(sa, co);


此时按照下面这样写更好:

static <T> void fromArrayToCollection(<? extends T> from,Collection<T> to){
for(T ele:from){
c.add(ele);
}
}


泛型方法和类型通配符?的区别:

通配符是被设计用来支持灵活的子类化的。泛型方法允许类型形参被用来表示方法的一个或多个参数之间的类型依赖关系,或者方法返回值与参数之间的类型依赖关系,如果没有这样的类型依赖关系,就不该使用泛型方法。而且类型通配符既可以在方法签名中定义形参的类型,也可以用于定义变量的类型;但是泛型方法中的类型形参必须在对方方法中显示声明。


java7新增的“菱形语法”允许调用构造器时在构造器后使用一对尖括号代表泛型信息。但是如果程序显示指定了泛型构造其中声明的类型形参的实际类型,就不可以使用“菱形语法”。

public <T> MyClass(T t){
System.out.println("t参数的值为:"+t);
}

MyClass<String> mc1=new MyClass<>(3);
MyClass<String> mc2=new <Integer>MyClass<String>(5);
MyClass<String> mc3=new <Integer>MyClass<>(5);//wrong


擦除和转换

如果没有为泛型类指定实际的类型参数,则该类型参数被称为raw type(原始类型),默认是声明该类型参数时的第一个上限类型。当把一个具有泛型信息的对象赋给另一个没有泛型信息的变量时,所有在尖括号之间的类型信息将都被扔掉。例如:

class Apple<T extends Number>{
}

Apple<Integer> a = new Apple<>(6);
Integer as = a.getSize();
//把a对象赋给Apple变量,丢失尖括号里的类型信息
Apple b = a;
// b只知道size的类型是Number
Number size1 = b.getSize();
// 下面代码引起编译错误
Integer size2 = b.getSize();


泛型和数组

java泛型一个很重要的设计原则:

如果一段代码在编译时没有提出”[unchecked]未经检查的转换”警告,则程序在运行时不会引发ClassCastException异常。

所以,数组元素类型不能包含类型变量或类型形参,除非是无上限的类型通配符。但是可以声明元素类型包含类型变量或类型形参的数组,也就是说,只能声明
List<String>[]
数组,但是不能创建
ArrayList<String>[10]
这样的数组对象。

如果可以,下面代码不会发生警告,但是会引发ClassCastException异常:

List<String>[] lsa=new ArrayList<String>[10];
Object[] oa=(Object[])lsa;
List<Integer> li=new ArrayList<Integer>();
li.add(new Integer(3));
oa[1]=li;
String s=lsa[1].get(0);


这段代码可以写成下面的形式:

List<?>[] lsa=new ArrayList<?>[10];
Object[] oa=lsa;
List<Integer> li=new ArrayList<Integer>();
li.add(new Integer(3));
oa[1]=li;
Object target=lsa[1].get(0);
if(target instanceof String){
String s=(String) target;
}


注:上面的内容是我看书总结而来,方便自己记忆以及随时观看而已~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: