java泛型学习和实践(1)

引言

JDK1.5之后引入了泛型,泛型刚开始接触时比较难理解,经过慢慢学习,有一些收获,现总结下,既可以加深自身理解,也可以帮助他人。

由于本人水平有限,难免有不对的地方,还请指正。废话不多说,下面正式开始。

为了更直观的说明泛型,现假设一个司机开车的场景。我们需要3个类,Driver(司机)、Ford(福特)、Buick(别克)。

第一版

第一版本代码如下:

public class Buick {

    public void run(){
        System.out.println("buick run");
    }

    public void autoRun(){
        System.out.println("buick auto-run");
    }

}
public class Ford {

    public void run(){
        System.out.println("ford run");
    }

    public void fly(){
        System.out.println("ford fly");
    }
}
public class Driver {

    private Object car;

    public void drive(Object car){
        this.car = car;
        if(car instanceof Buick){
            System.out.println("I am driving a " + car);
            ((Buick)car).run();
        }else if(car instanceof Ford){
            System.out.println("I am driving a " + car);
            ((Ford)car).run();
        }
    }

    public Object getDrivingCar(){
        return car;
    }

    public static void main(String[] args) {
        Driver driver1 = new Driver();
        driver1.drive(new Ford());
        Driver driver2 = new Driver();
        driver2.drive(new Buick());

        //...执行其它业务逻辑

        /** 获取司机1开的福特车执行fly方法 */
        ((Ford)driver1.getDrivingCar()).fly();

        /** 获取司机2开的别克车执行autoRun方法 */
        ((Buick)driver2.getDrivingCar()).autoRun();

    }
}

简单说明下三个类。Buick类和Ford类都含有run方法,Buick类有autoRun方法,Ford类有fly方法。

Driver类有一个drive方法和getDrivingCar方法及内部属性car。main静态方法是演示示例。

通过上面的演示示例可以发现,在调用Driver实例的drive方法时,实际参数向上转型为Object对象,而在调用getDrivingCar方法时,又需要向下转型为具体的car类来调用其特定的方法。那么这种方式会有什么问题呢?

问题是显而易见的,drive方法的参数类型和getDrivingCar的返回值类型是一样的,但由于要支持不同的car,向上转型为Object类型(没有定义为具体的Buick类型或Ford类型),这样外部在调用getDrivingCar时,返回的是一般化的Object,这样就失去了具体类的特征,需要外部强制类型转换。这种显示强制转换方式很容易产生ClassCastException异常(不小心把driver2写成driver1时执行代码时会抛出该异常),并且这种编程错误不能在编译期间发现,而要等到运行期间才能发现,有可能产生隐藏BUG。并且这种方式也不合理,Driver类的使用门槛过高,需要外部代码很小心的处理来防止出现错误,例如在强制转换前用instanceof进行判断。那么有没更好的方式呢?

第二版

第二版本代码如下(只修改Driver类):

public class Driver<T> {

    private T car;

    public void drive(T car){
        this.car = car;
        if(car instanceof Buick){
            System.out.println("I am driving a " + car);
            ((Buick)car).run();
        }else if(car instanceof Ford){
            System.out.println("I am driving a " + car);
            ((Ford)car).run();
        }
    }

    public T getDrivingCar(){
        return car;
    }

    public static void main(String[] args) {
        Driver<Ford> driver1 = new Driver<Ford>();
        driver1.drive(new Ford());
        Driver<Buick> driver2 = new Driver<Buick>();
        driver2.drive(new Buick());

        //...执行其它业务逻辑

        /** 获取司机1开的福特车执行fly方法 */
        driver1.getDrivingCar().fly();

        /** 获取司机2开的别克车执行autoRun方法 */
        driver2.getDrivingCar().autoRun();

    }
}

Driver类声明中使用的<T>,即泛型T。你可以把T类型理解成代表任意一种具体类型的类型。同时修改drive方法参数类型和getDrivingCar方法返回值类型及私有属性car都是T类型。那么这样做有什么好处呢?

从main方法的演示使用代码可以看出,在实例化Driver类时,需要指定具体的T类型(driver1中的Ford和driver2中Buick)。实例化之后,调用getDrivingCar方法的返回类型也是相应的具体类型,从而可以直接调用具体类型特定的方法。

比较第一版的Driver类,改进版的Driver类,只需要使用者在类实例化时指定具体的T类型,在后续操作中不需要进行强制类型转换,从而减少出错的可能性,提高了代码的稳健性。

通过泛型T,很轻松对drive方法参数类型、getDrivingCar方法返回值类型及私有属性car类型这三处地方的类型进行了统一,且不失具体性(不像第一版将car申明为Object类型)不需要额外的代码成本,类结构看上去清晰明了。对外部使用者,使用门槛也低,不会出现不小心弄错类型的错误(想出现也不可能,如果指定了错误类型,会在编译期间提示错误)

上面的示例演示了泛型最基本的用法。通过泛型,很容易在类的成员变量或方法参数或方法返回值之间声明为统一类型且不失类型的具体性(在类定义中是泛型,但在实例化时需要指定具体的类型)

联系实际

现在大家应该对泛型有了一个基本的了解。下面举个实例项目中的例子,更进一步说明问题。

项目中一般都需要分页类,简化版如下

/**
 * <简单分页类>
 */
public class SimplePage {

    /** 每页显示的记录数*/
    private int numPerPage;

    /** 总记录数 */
    private int totalCount;

    /** 当前页 */
    private int pageNum;

    //...省略其它方法
}
/**
 * <带内容分页类>
 */
public class Pageable<E> extends SimplePage {

    /**
     * 本页内容
     */
    private List<E> list;

    /**
     * <设置内容>
     * @param list
     */
    public void setList(List<E> list) {
        this.list = list;
    }

    /**
     * <获取内容>
     * @return
     */
    public List<E> getList() {
        return list;
    }
}

SimplePage:简单分页类,包含当前页,每页显示记录数,总记录数等基本成员属性。

Pageable:业务数据分页类,继续自简单分页类,带有本页数据信息。

比如要分面查询用户信息时,假设有User类封装用户信息。使用代码如下:

        Pageable<User> pageInfo = new Pageable<User>();

        //...分页获取出用户信息,并调用setList方法将用户信息放入pageInfo实例中

        /** 其它地方获取到userList,进行其它业务操作 */
        List<User> userList = pageInfo.getList();

使用起来非常简单,今天就码到这里了,未完待续。

码字不易,转载请标明出处

时间: 2024-09-28 09:32:46

java泛型学习和实践(1)的相关文章

java泛型学习和实践(4)

引言 前三节讲述了泛型常见声明及使用,泛型既可以在类上进行声明,也可以在单个方法上进行声明,并分别对这两种情况进行了总结.下面来学习下泛型扩展知识. 延用前面的Runnable接口.Buick类.Ford类.Driver类,新增加一个汽车容器类CarContainer 第一版 代码如下: public interface Runnable { public void run(); } public class Buick implements Runnable { @Override publi

java 泛型学习

http://blog.csdn.net/archie2010/article/details/6232228 学习集合框架的时候经常用hasmap<Integer,Integer>就是泛型,c++里面叫模板,其实我是想研究一下迭代器模式的.睡觉,明天再说. 1 import java.util.ArrayList; 2 import java.util.Collection; 3 4 class A< T extends Collection> 5 { 6 private T x

java泛型学习(2)

一:深入泛型使用.主要是父类和子类存在泛型的demo /** * 父类为泛型类 * @author 尚晓飞 * @date 2014-7-15 下午7:31:25 * * * 父类和子类的泛型. * [泛型的具体声明] * (1)子类直接声明具体类型 * (2)使用时指定具体类型(new 对象时) * (3)子类泛型>=父类泛型(个数,类型,顺序无关) * * [泛型的确定] * (1)属性:在父类中,泛型随父类泛型而定 * 子类中,泛型随子类泛型而定 * (2)重写方法中: * 泛型全部随父类

Java泛型学习笔记--Java泛型和C#泛型比较学习(一)

总结Java的泛型前,先简单的介绍下C#的泛型,通过对比,比较学习Java泛型的目的和设计意图.C#泛型是C#语言2.0和通用语言运行时(CLR)同时支持的一个特性(这一点是导致C#泛型和Java泛型区别的最大原因,后面会介绍).C#泛型在.NET CLR支持为.NET框架引入参数化变量支持.C#泛型更类似C++模板,可以理解,C#泛型实际上可以理解为类的模板类.我们通过代码实例来看C# 2.0泛型解决的问题,首先,我们通过一个没有泛型的迭代器的代码示例说起,代码实现如下: interface

Java泛型学习笔记 - (七)浅析泛型中通配符的使用

一.基本概念:在学习Java泛型的过程中, 通配符是较难理解的一部分. 主要有以下三类:1. 无边界的通配符(Unbounded Wildcards), 就是<?>, 比如List<?>. 无边界的通配符的主要作用就是让泛型能够接受未知类型的数据. 2. 固定上边界的通配符(Upper Bounded Wildcards): 使用固定上边界的通配符的泛型, 就能够接受指定类及其子类类型的数据. 要声明使用该类通配符, 采用<? extends E>的形式, 这里的E就是

Java泛型学习

先引用一段Java编程思想中的一段话 Java泛型的核心概念:告诉编译器你想使用什么类型,然后编译器帮你处理一切细节. 关于类型推断: 类型推断只对赋值操作有效,其他时候并不起作用.如果你见过一个泛型方法调用的结果 作为参数传递给另一个方法,这时编译器不会执行类型推断.在这种情况下,编译器认为:调用泛型 方法后,其返回值被付给了一个Object变量.当然现在在Java8先已经可以了

Java泛型学习笔记 - (六)泛型的继承

在学习继承的时候, 我们已经知道可以将一个子类的对象赋值给其父类的对象, 也就是父类引用指向子类对象, 如: 1 Object obj = new Integer(10); 这其实就是面向对象编程中的is-a关系. 既然上面的代码正确, 那么在泛型中, 也可以使用如下代码: 1 public class Box<T> { 2 private T obj; 3 4 public Box() {} 5 6 public T getObj() { 7 return obj; 8 } 9 10 pub

Java泛型学习笔记 - (二)泛型类

1. 我们先写一个没有泛型的类Box: 1 public class Box { 2 3 private Object obj; 4 5 public Box() {} 6 7 public Object getObj() { 8 return obj; 9 } 10 11 public void setObj(Object obj) { 12 this.obj = obj; 13 } 14 15 public Box(Object obj) { 16 super(); 17 this.obj

Java泛型学习笔记 - (三)泛型方法

泛型方法其实和泛型类差不多, 就是把泛型定义在方法上, 格式大概就是: public <类型参数> 返回类型 方法名(泛型类型 变量名) {...}泛型方法又分为动态方法和静态方法,:1. 动态泛型方法其实在前一篇博文中我已经用到了, 1 public class Box<T> { 2 3 private T obj; 4 5 public Box() {} 6 7 public T getObj() { 8 return obj; 9 } 10 11 public void se