Java基础泛型篇一

来源:互联网 发布:js定义一个json数组 编辑:程序博客网 时间:2024/06/06 17:13

泛型基础

1.定义

 Java的的泛型是从Java的jdk1.5之后出现的,一种参数化的复杂数据类型。泛型的出现是由当前的需求所迫。  java泛型的应用可以提高的代码的复用性,同时泛型提供了类型检查,减少了数据的类型转换,同时保证了类型安全。看下面这这种情况;
List list = new ArrayList();list.add("好人一生平安");//可以通过编译list.add(new Integer(1));//可以通过编译for(Object object : list){    LogUtil.log("输入值",object + "");//运行的时候报这个异常ClassCastException}
这个很明显是因为java的类型之间的数据转换异常,是将Integer类型的对象转换为String的时候抛出异常了。
如果在java5之后,我们采用泛型的方式就可以避免出现这个问题。
List<String> list = new ArrayList();list.add("好人一生平安");//可以通过编译list.add(new Integer(1));//不能编译for(Object object : list){    LogUtil.log("输入值",object + "");}
看到第三行代码了吗,直接不能通过编译,因为我们将这个集合的类型定义成了String类型,你不能给String 类型的集合中添加Integer类型的数据,所以编译的时候直接报错。我们可以看看List的源码如下:
/** * A {@code List} is a collection which maintains an ordering for its elements. Every * element in the {@code List} has an index. Each element can thus be accessed by its * index, with the first index being zero. Normally, {@code List}s allow duplicate * elements, as compared to Sets, where elements have to be unique. */public interface List<E> extends Collection<E> {    /**     * Inserts the specified object into this {@code List} at the specified location.     * The object is inserted before the current element at the specified     * location. If the location is equal to the size of this {@code List}, the object     * is added at the end. If the location is smaller than the size of this     * {@code List}, then all elements beyond the specified location are moved by one     * position towards the end of the {@code List}.     *     * @param location     *            the index at which to insert.     * @param object     *            the object to add.     * @throws UnsupportedOperationException     *                if adding to this {@code List} is not supported.     * @throws ClassCastException     *                if the class of the object is inappropriate for this     *                {@code List}.     * @throws IllegalArgumentException     *                if the object cannot be added to this {@code List}.     * @throws IndexOutOfBoundsException     *                if {@code location < 0 || location > size()}     */    public void add(int location, E object);
看到没有这个接口提供了是采用泛型的方式来定义的,继承自Collection<E>采用也是泛型的形式,一直到Iterable<E>。
 * Methods marked with (optional) can throw an * {@code UnsupportedOperationException} if the underlying collection doesn't * support that method. */public interface Collection<E> extends Iterable<E> {

所以采用泛型的就会在编译的时候提示你异常,而不会再运行时才提示你,这样保证了数据的一致性。

泛型的分类。泛型分为三种,泛型方法,泛型类,泛型接口三种。

2.泛型类

创建一个普通的类。然后调用它的方法
public class Book {    private Object object;    public Object getObject() {        return object;    }    public void setObject(Object object) {        this.object = object;    }

Book book = new Book();book.setObject("好好学习,天天向上");String value = (String) book.getObject();//运行时会在这里会抛出异常,不能将对象转成String

这种原始的使用方式和上面的相似,没有使用泛型的时候,在程序运行阶段报出异常。我们看看类的泛型使用。

 */public class Book<T> {    private T object;    public T getObject() {        return object;    }    public void setObject(T object) {        this.object = object;    }


Book<String> book1 = new Book<>();book1.setObject("这个是String类型");//book1.setObject(12);//这里直接在编译的时候报错。String result = book1.getObject();LogUtil.log("泛型",result);Book book = new Book();book.setObject("好好学习,天天向上");String value = (String) book.getObject();//在这里就不会输出错误LogUtil.log("输入信息==",value);book.setObject(7);int value_int = (int) book.getObject();//在这里就不会输出错误LogUtil.log("输入信息==",value_int + "");

现在看,book1对象定义为String类型,所以不能输入Integer类型的数据,因为定义为泛型就再不需要强制转换了。book对象没有定义为那种类型,因为是泛型,我们可以在下面设置String 和Int都不会报错,这样的话还是要进行强制转换。这个就是泛型类的简单实用。

3.泛型接口

泛型接口的定义很简单,
public interface FanInterface<T> {   void setValue(T t);    T getT();}
实现接口的时候要定义泛型的类型,不然不能使用。

public class GetVauleImpl implements FanInterface<Integer> {    @Override    public void setValue(Integer o) {        }    @Override    public Integer getT() {        return null;    }}
当创建了带泛型声明的接口或者实现类时,可以为接口创建相应的实现类,或者从父类派生子类,但是在使用这些接口或者父类时不能再包含类型形参。指定一个类型,或者不指定,但是不能写<T>。

4.类型通配符

4.1通配符的定义

通配符:是指可以指定任何的类型,对于数据类型不确定的值的时候我们就可以采用通配符。
例如:下面的代码。
public class TongPeiFu {    public void invoke(List<Object> list){//函数参数        int count=list.size();        for(int i=0;i<count;i++){            System.out.println(list.get(i));        }    }  void  task(){      List<String> list=new ArrayList<String>();      list.add("a");      invoke(list);//编译的时候就报错了,因为List<String>不是List<Object>的子类,    }}
如上代码编译的时候就报错了,因为函数的参数形式是List<Object>类型的,而实参是List<String>类型的,但是需要注意的一点是List<String >的不是List<Object>的子类,所以编译的时候操作了,所以在函数参数不确定的情况,就引出了通配符的使用。java中类型通配符用“?”标示;类型通配符是所有泛型List的父类。在定义的使用用?标示参数就行,上面代码改成这样就不会报错了;
 */public class TongPeiFu {    public void invoke(List<?> list){//函数参数        int count=list.size();        for(int i=0;i<count;i++){            System.out.println(list.get(i));        }    }  void  task(){      List<String> list=new ArrayList<String>();      list.add("a");      invoke(list);//不会报错,    }}
当直接采用List<?>的话,默认就是所有List的父类。
还有如下实例:
//采用通配符    public static void getData(Box<?> data) {        System.out.println("data :" + data.getT());    }
Box<String> box = new Box<>("测试泛型");LogUtil.log("返回的数据结构===", box.getT());Box<Number> box_number = new Box<Number>(123);LogUtil.log("返回的数据==", box_number.getT() + "");Box<Integer> box_int = new Box<>(456);LogUtil.log("返回的数据==", box_int.getT() + "");getData(box);getData(box_number);getData(box_int);
//这种就是在使用的时候才设定他实参的类型。



4.2通配符的上限

上限是指这定子类是以特定父类下面的泛型。采用如下:<T extends superclass>标示
//    这个是extend的用法,指定上限。    public static void getSubNumberData(Box<? extends Number> data) {        System.out.println("data :" + data.getT());    }
这个是指定上限为Number数字类型,所以在使用的时候要
Box<Number> box_number = new Box<Number>(123);LogUtil.log("返回的数据==", box_number.getT() + "");Box<Integer> box_int = new Box<>(456);LogUtil.log("返回的数据==", box_int.getT() + "");
getSubNumberData(box_number);getSubNumberData(box_int);//不会出现编译异常getSubNumberData(box);//出现编译异常
这样就指定了上限是Number,实参使用的时候只要是Number或者是其子类的话都不会报错。但是采用String类型的就会报错。
采用这种方式也会出现问题。
//    这个是extend的用法,指定上限。上限为Integer的时候。
    public static void getSubNumberData(Box<? extends Integer> data) {        System.out.println("data :" + data.getT());    }

Box<Number> box_number = new Box<Number>(123);LogUtil.log("返回的数据==", box_number.getT() + "");Box<Integer> box_int = new Box<>(456);LogUtil.log("返回的数据==", box_int.getT() + "");getSubNumberData(box_number);//编译的时候出现异常getSubNumberData(box_int);//不会出现编译异常
//看到没有当上限定义为Integer的时候的时候,设置实参类型为Number类型的话就会报错。
坦白的说通配符的上限就是制定父类的类型。

4.3 通配符下限

其实通配符的下限就是上限的反义词,指定下限意味着限定子类。使用的时候要注意。
//泛型的用法 限定下限为Integer类型    public static void getUpperNumberData(Box<? super Integer> data) {    System.out.println("data :" + data.getT());}
Box<Number> box_number = new Box<Number>(123);LogUtil.log("返回的数据==", box_number.getT() + "");Box<Integer> box_int = new Box<>(456);LogUtil.log("返回的数据==", box_int.getT() + "");getUpperNumberData(box_number);getUpperNumberData(box_int);//都可以通过
当改为这个时
//泛型的用法    public static void getUpperNumberData(Box<? super Number> data) {    System.out.println("data :" + data.getT());}
Box<Number> box_number = new Box<Number>(123);LogUtil.log("返回的数据==", box_number.getT() + "");Box<Integer> box_int = new Box<>(456);LogUtil.log("返回的数据==", box_int.getT() + "");getUpperNumberData(box_number);getUpperNumberData(box_int);//不能通过编译了
//看到没有不能通过编译了。

5. 泛型方法

上面是定义接口和类的时候声明类型参数,在该类的方法定义、变量定义和接口的方法定义、变量的定义中,该类型形参可以当作普通类型来使用。如果我们的类没有声明类型形参,但是方法却想定义类型形参数的话,那么我们就可以使用泛型方法。
//泛型方法public <E> void genericmethod(List<E> a){    for(E t:a)        System.out.println(t.toString());}
List<String> list=new ArrayList<>();list.add("好好好");list.add("老司机等等我");list.add("滴,学生卡");list.add("我日等我下");fanMethod(list);List<Integer> list1=new ArrayList<>();list1.add(1);list1.add(2);list1.add(3);list1.add(4);fanMethod(list1);
使用的时候就这样用。
泛型的使用简单的就这么多。但是在实际的项目中更多的使用的是符合类型的泛型。

基本的用法学习完后,觉得定义泛型的类型和通配符的类型都是不确定,假如定义了一个泛型类或者泛型方法,又如何确定能类或者方法里做什么操作呢。回想看到其他用泛型一般都是在定义抽象类或者接口的时候使用,用于子类或者实现类的扩展 。因为是一般的类中使用泛型没有任何意义,因为泛型的类型是未知的,不能做任何的操作!!!

所以说泛型的设计是要考虑的代码的扩展和复用。要不然是没有意义的。

如下面的代码:

public <T> void getData(T t1,T t2){    if(t1.contains(t2)){//编译不能通过        LogUtil.log("t1 包含t2");    }}//这样的话编译是不能通过的。
但是采用这样的话就可以操作

public <T extends  String> void getData(T t1,T t2){    if(t1.contains(t2)){//编译不能通过        LogUtil.log("t1 包含t2");    }}
这样的话就采用通配符就可以很好的实现代码的复用了。采用通配符限定一些类型来实现相关的功能。


6.总结

泛型也算是Java比较新的知识,熟练的运用泛型能够使我们的代码更加的灵活,更加的有扩展性,也能使得代码的重用性变的更好。所以在开发过程中结合项目实际,合理使用泛型,写出优雅的代码。这个才是程序员的最大追求。

1 0
原创粉丝点击