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

java基础(七)之快速理解泛型

2017-09-22 15:58 411 查看
引言:

泛型在日常开发中并不常见,但是在很多开发框架中却非常常见,学习泛型最好的方法就是阅读jdk源码,泛型在集合这部分大量应用。

一.泛型的出现原因

下面从两段段代码中来进行理解泛型出现的原因:

import java.util.ArrayList;
public class GenericTest {
public static void main(String[] args) {
ArrayList list=new ArrayList();
list.add(2);
list.add("3");
int str=(Integer)list.get(1);
System.out.println(str);
}
}


这段代码执行后会报ClassCastException异常,即类型转换异常,默认集合中元素类型都是Object类型,当你对集合中元素进行操作时,必须知道该元素是什么类型,再进行转型,不然会导致类型转换出错。这段代码在编译阶段是不进行错误检查的,只有在运行时才能报错。

//超级会员
class SuperUser{
String authority="";
String name="";
int credit=0;
}
//普通用户
class CommonUser{
String authority="";
String name="";
}
//封装的的普通用户的数据列表
class SuperUserData{
int status=0;
List<SuperUser> su=null;
}
//封装的的普通用户的数据列表
class CommonUserData{
int status=0;
List<CommonUser> su=null;
}


我们发现代码的重用性存在问题,最后封装的数据类中集合元素只是类型不同而已。因此我们就希望能否向参数化形参一样,能否参数化类型?参数化形参的出现是为了提高代码的重用性。那么参数化类型部分为了提高代码的可重用性。

可以将上述的2个数据封装类,写成通用的数据容器类

class UserData<T>{
int status=0;
//参数化数据类型
T data=null;
}


泛型的好处:

1.用来在代码编译阶段就强制进行数据类型检查,避免出现类型转化问题。同时也避免了大量使用类型转换这样的可能出错的代码段。

2.提高程序的可重用性

二.泛型的机制

只有理解泛型的机制才能更好的使用泛型,泛型只是java提供的一层经过包装的语法糖。

1.类型擦除

Java的泛型是伪泛型。为什么说Java的泛型是伪泛型呢?因为,在编译期间,所有的泛型信息都会被擦除掉。正确理解泛型概念的首要前提是理解类型擦除(type erasure)。Java中的泛型基本上都是在编译器这个层次来实现的。在生成的Java字节码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会在编译器在编译的时候去掉。这个过程就称为类型擦除。

public class MyTest {
public static void main(String[] args) {
ArrayList<String> arrayList1=new ArrayList<String>();
arrayList1.add("abc");
ArrayList<Integer> arrayList2=new ArrayList<Int
db0a
eger>();
arrayList2.add(123);
System.out.println(arrayList1.getClass()==arrayList2.getClass());
}
}


如在代码中定义的List和List等类型,在编译后都会编程List。JVM看到的只是List,而由泛型附加的类型信息对JVM来说是不可见的。Java编译器会在编译时尽可能的发现可能出错的地方,但是仍然无法避免在运行时刻出现类型转换异常的情况。类型擦除也是Java的泛型实现方法与C++模版机制实现方式之间的重要区别。

2.类型擦除后保留的原始类型

原始类型(raw type)就是擦除去了泛型信息,最后在字节码中的类型变量的真正类型。无论何时定义一个泛型类型,相应的原始类型都会被自动地提供。类型变量被擦除,并使用其限定类型(无限定的变量用替换).

class Pair<T> {
private T value;
public T getValue() {
return value;
}
public void setValue(T  value) {
this.value = value;
}
}


Pair的原始类型为:

class Pair {
private Object value;
public Object getValue() {
return value;
}
public void setValue(Object  value) {
this.value = value;
}
}


因为在Pair中,T是一个无限定的类型变量,所以用Object替换。其结果就是一个普通的类,如同泛型加入java变成语言之前已经实现的那样。在程序中可以包含不同类型的Pair,如Pair或Pair,但是,擦除类型后它们就成为原始的Pair类型了,原始类型都是Object。

3.类型转换

因为类型擦除的问题,所以所有的泛型类型变量最后都会被替换为原始类型。这样就引起了一个问题,既然都被替换为原始类型,那么为什么我们在获取的时候,不需要进行强制类型转换。这部分其实jvm暗中已经帮我们做了。这点可以从java集合中的源码就可以知道。

看下ArrayList和get方法:

public E get(int index) {
RangeCheck(index);
return (E) elementData[index];
}


三.泛型基本使用

1.泛型接口

interface Iuser<T>{
//泛型接口中泛型方法
public T getUser();
}


2.泛型类

class  MyUser<T>{
T data=null;
int id=0;
//泛型类中泛型方法
public T getUser(){
return data;
}
}


3.普通类中泛型方法

class  MyUser{
int id=0;
//普通类中泛型方法,<T>是类型的定义
public <T> T getUser(){
T data=null;
return data;
}
}


注:这里的T仅仅是一个占位符,可以使用任何的字母符号替代。但一般程序开发都用K,V,E,T这四个最常用的符号。

4.泛型通配符 ?

public static void fun(List<Object> list){
System.out.println(list);
}
List<String> list1 = new ArrayList<String>();
List<Integer> list2 = new ArrayList<Integer>();
fun(list1);//编译不通过
fun(list2);//编译不通过


下面通配符就能出场了。

public static void fun(List<?> list) {
System.out.println(list);
}


下面介绍几种通配符用法:

1)通配符只能出现在引用的定义中,而不能出现在创建对象中。

//不能出现在创建对象中,无法编译通过
List<?> list=new ArrayList<?>(),
//引用可以
ArrayList<?> list = null,


2)带有下边界的通配符

//即“?”只能被赋值为Number或其子类型。
List<? extends Number> list;


3)带有上边界的通配符

//即“?”只能被赋值为Integer或其父类型。
List<? super Integer> list;


四.源码分析

我们以ArrayList实现为例来说明泛型的应用。

ArrayList的继承关系如下:

public class ArrayList<E> extends AbstractList<E> implements List<E>{}


add方法如下

public boolean add(E e) {
ensureCapacityInternal(size + 1);  // Increments modCount!!
elementData[size++] = e;
return true;
}


下面就是找到elementData到底是啥?

private transient Object[] elementData;


原来底层就是Object数组。

下面我们看下get方法

public E get(int index) {
rangeCheck(index);

return elementData(index);
}


重点就在于elementData()方法,继续找到该方法

E elementData(int index) {
return (E) elementData[index];
}


原来,底层就是用Object数组实现的,当使用相应的类型时,最后还是进行类型转换,只不过java提供了语法糖,底层为我们进行了类型转换。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: