接口,泛型,可变参数在代码向上抽去中的应用探究

  JAVA作为一种面向对象的语言,类和对象可以说是搭建起JAVA体系的基本框架,我们可以将类看做是对众多实体对象共性的向上抽取,而接口则是对类共性的向上抽取,我将接口理解为一种规则,一种规范,同时也是多态的应用中我们作为形式参数传递的最顶层的父类,因此接口的功能非常强大(能装B),我们在sun公司定义的API中可以经常看到它的身影,它也是架构师手中搭建框架的利器,因此以后我们会经常使用到它(能装B).

-------------------------------------------------------------------------------------------------------------------------------------

  程序,功能块的向上抽取,是简化代码和增强扩展性的基本思路,而接口,泛型和可变参数则是我们向上抽取,所要用到的基本工具,话不多说,下面我用三个简单的例子来进行演示:

  案例一:

//创建一个Student对象,对外提供set和get方法
public class Student {
    private String name;
    private int age;
    private int num;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public int getNum() {
        return num;
    }
    public void setNum(int num) {
        this.num = num;
    }

}
public static void main(String[] args) {
        //在这里我们创建一个treeset集合,用来存储Student对象,我们知道牛X的二叉树算法为我们提供了强大的排序功能,但是具体的排序方式需
        //要我们用户自己来定义,所以这里我以匿名内部内的方式传入一个比较器对象(compartor接口的实现子类对象),来定义我所需要的具体的排序方式
        TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                int flag = o1.getNum() - o2.getNum();
                return flag == 0? 1 : flag;
            }
        });
    }

  在这个案例中,API的设计者想要将自动排序的功能进行向上抽取,但是又不知道,用户想要以什么样的形式进行排序,是按照学生年龄呢,姓名呢,学号能还是三者结合进行排序,所以设计者就定义出一个comparator接口,在里面定义了一个compare()的抽象方法,再将这个接口以形式参数的形式由构造传入treeset集合中,这样我们用户在创建treeset集合的时候如果想要自定义的它的排序功能就必须,传入一个comparator的实现子类对象,而它的实现子类则必须重写compare方法.在这个案例中,API的设计者以接口和多态相结合的方式完成了自定义排序功能的向上抽取

  案例二:这里我以JDBC与数据库连接中,对查找方法的向上抽取为例

//自定义一个接口,其中包含具体处理结果集的的抽象方法handle,将结果集以参数形式传入
public interface ResultSetHandler<T> {
    public T handle(ResultSet rs) throws SQLException;
}
//我们想要将查询方法进行向上提取,有三个问题需要解决:
    //1.sql语句由用户传入
    //2.我们对查询参数的设定要根据sql语句而定,我们无法确定参数的具体个数和类型,因此这里我们传入一个object类型的可变参数,可变参数的本质
    //就是一个集合
    //3.这也是最难解决的一点,根据不同的查询方式,会返回不同的结果集,我们对不同的结果集的封装和返回的形式也不相同,所以这里我们传入一个自定义的
    //接口ResultSetHandler,以这种方式将处理结果集的工作交给调用者来完成
    //因为返回值的类型并不确定是对象还是集合,所以这里引入了泛型
    public static <T> T query(String sql, ResultSetHandler<T> rsh,
            Object... args) {
        //提全局变量
        Connection conn = null;
        PreparedStatement stmt = null;
        ResultSet rs = null;
        try {
            //这里通过我自定义的一个工具类来获取connection对象
            conn = JDBCUtils.getConnection();
            // 预编译SQL:
            stmt = conn.prepareStatement(sql);
            //通过PreparedStatement对象参数元数据对象
            ParameterMetaData metaData = stmt.getParameterMetaData();
            // 获得参数的个数:
            int count = metaData.getParameterCount();
            for (int i = 1; i <= count; i++) {
                stmt.setObject(i, args[i - 1]);
            }
            // 执行SQL:
            rs = stmt.executeQuery();
            // 数据封装:通过调用接口中交由调用者自己重写的handle方法对获取到的结果集进行封装和返回
            T t = rsh.handle(rs);
            return t;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.release(rs, stmt, conn);
        }
        return null;
    }
// 测试查询一个账户:这里我们以匿名内部类的形式传入ResultSetHandler的实现子类对象,并重写了handle方法,对结果集进行了具体的处理
        Account account = MyDBUtils.query("select * from account where id = ?", new ResultSetHandler<Account>() {
            @Override
            public Account handle(ResultSet rs) throws SQLException {
                Account account = new Account();
                if(rs.next()){
                    account.setId(rs.getInt("id"));
                    account.setName(rs.getString("name"));
                    account.setMoney(rs.getDouble("money"));
                }
                return account;
            }
        }, 2);
        System.out.println(account);

 具体的操作在代码中已经写得很具体,这里就不再赘述

   案例三:

  以前在我大学里面有一个煎饼店,只卖山东杂粮煎饼和台湾手抓饼,但是生意却好得不得了,几乎天天从早上六点多到晚上十点多都有一大群人在排队,想吃不排上个十几分钟队根本吃不上,所以我再举一个煎饼店的例子,希望可以帮助大家理解.

  在这里,我们将摊煎饼看作一个功能块,在将这个功能块向上抽取之前,每次想要吃煎饼都必须要我自己去准备材料,自己摊煎饼非常麻烦,所以我考虑将这个功能块向上抽取(请一个会摊煎饼的阿姨来帮我摊煎饼),这个时候煎饼阿姨就会遇到两个问题,第一个:煎饼有杂粮煎饼和手抓饼,味道有加辣和不加辣,不同的的煎饼还有不同的酱可以选,煎饼阿姨没办法在用户到来之前知道应该摊哪一种煎饼(每个人的煎饼都是DIY的);第二个:在确定具体的煎饼类型的做法之前煎饼阿姨无法确定材料的种类和数量.

public interface Type {
    //在接口中定义一个具体设置煎饼类型的抽象方法
    public void setType();
}

  针对第一个问题我们就可以定义一个接口,来具体设置煎饼的类型,然后将接口以形式参数的形式传入摊煎饼的方法中,当我们来调用摊煎饼方法的时候就需要传入这个接口的实现子类对象,在这个实现子类中必须重写setType()方法,即将设置煎饼类型口味的工作以接口的形式抛给了调用者

public class PancakeLady {

    public void madePancake(Type tp ,String...data) {
        //调用接口中的方法
        tp.setType();
        //获取到可变参数中的数据进行遍历打印
        for (String str : data) {
            System.out.print(str + ",");
        }
    }
}

  对于第二个问题,我们采用可变参数的形式来就收任意类型,任意个数的参数(这里为了简便,设置为了String类型)

public static void main(String[] args) {
        PancakeLady pl = new PancakeLady();
        pl.madePancake(new Type() {
            @Override
            public void setType() {
                // TODO Auto-generated method stub
                System.out.print("这是一个加辣的山东杂粮煎饼,里面有:");
            }
        }, "鸡蛋","火腿","里脊");
    }
    //这是一个加辣的山东杂粮煎饼,里面有:鸡蛋,火腿,里脊,

最后我们在调用的时候我们以匿名内部类的形式传入Type接口的实现子类,来完成了煎饼类型的定义

------------------------------------------------------------------------------------------------------------------------------

  将不能确定的问题抛向调用者,也是最大化调用者权限的体现,JAVA中的异常处理机制采用的就是这种方式

时间: 2024-08-02 05:28:25

接口,泛型,可变参数在代码向上抽去中的应用探究的相关文章

java map接口,可变参数,Collections集合工具类

map接口的实现类存储成对的值,键--值.通过键来找到对应的值. Collection中的集合称为单列集合,Map中的集合称为双列集合 Map中常用的集合为HashMap集合.LinkedHashMap集合. HashMap<K,V>:存储数据采用的哈希表结构,元素的存取顺序不能保证一致.由于要保证键的唯一.不重复,需要重写键的hashCode()方法.equals()方法. LinkedHashMap<K,V>:HashMap下有个子类LinkedHashMap,存储数据采用的哈

Java面向对象_增强for可变参数与代码块

1.foreach循环 for(类型 变量名称:数组或集合){ //输出操作 } 2.可变参数:根据需要自动传入任意个数的参数,就是可变参数. 语法:返回值类型 方法名称(数据类型...参数名称){ } 例: 1 ublic class Practice14 { 2 3 /** 4 * @param args 5 */ 6 public static void main(String[] args) { 7 // TODO Auto-generated method stub 8 /*Strin

Java的LinkedHashSet、Map接口、可变参数、集合嵌套、

1.LinkedHashSet:(1)LinkedHashSet集合保证元素的存入和取出的顺序: package com.oracle.demo01; import java.util.HashSet; import java.util.LinkedHashSet; public class demo01 { public static void main(String[] args) { //不能存重复元素,但是LinkedHashSet是有序的. LinkedHashSet<String>

Effective Java 第三版——32.合理地结合泛型和可变参数

Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将近8年的时间,但随着Java 6,7,8,甚至9的发布,Java语言发生了深刻的变化. 在这里第一时间翻译成中文版.供大家学习分享之用. 32. 合理地结合泛型和可变参数 在Java 5中,可变参数方法(条目 53)和泛型都被添加到平台中,所以你可能希望它们能够正常交互; 可悲的是,他们并没有. 可变参数的目

[Java5新特性]可变参数

什么是可变参数 Java基础内容中,关于函数具有一种特性:重载,如果我们要完成多个数字想加的需求,可以按照以下代码完成: public class Demo { public int add(int a, int b) { return a + b; } public int add(int a, int b, int c) { return a + b + c; } public static void main(String[] args) { int sum1 = new Demo().a

PHP函数可变参数列表的具体实现方法介绍

PHP函数可变参数列表可以通过_get_args().func_num_args().func_get_arg()这三个函数来实现.我们下面就对此做了详细的介绍. AD:2014WOT全球软件技术峰会北京站 课程视频发布 也许对于PHP初级程序员来说,对于PHP函数并不能完全熟练的掌握.我们今天为大家介绍的PHP函数可变参数列表的实现方法主要是利用func_get_args().func_num_args().func_get_arg()这三个系统函数来实现的,其中func_get_args()

objective-c 可变参数

容易发现Cocoa Foundation 中提供了一些可变参数的方法,如: NSLog(NSString *format, ...) 在实际的编程实践中,我们也需要自己实现可变参数的方法.在Objc中,是依靠原生C库来的实现的. 请看示例: 1 - (void) doLog:(NSString *)formatStr, ... { 2 3 NSMutableArray *arr = [[NSMutableArray alloc]init]; 4 5 NSString *arg; 6 7 va_l

c 可变参数(variable argument)的原理及使用

本文主要介绍可变参数的函数使用,然后分析它的原理,程序员自己如何对它们实现和封装,最后是可能会出现的问题和避免措施. VA函数(variable argument function),参数个数可变函数,又称可变参数函数.C/C++编程中,系统提供给编程人员的va函数很少.*printf()/*scanf()系列函数,用于输入输出时格式化字符串:exec*()系列函数,用于在程序中执行外部文件(main(int argc,char*argv[]算不算呢,与其说main()也是一个可变参数函数,倒不

可变参数宏__VA_ARGS__

在 GNU C 中,宏可以接受可变数目的参数,就象函数一样,例如: #define pr_debug(fmt,arg...) \ printk(KERN_DEBUG fmt,##arg) 用可变参数宏(variadic macros)传递可变参数表 你可能很熟悉在函数中使用可变参数表,如: void printf(const char* format, -); 直到最近,可变参数表还是只能应用在真正的函数中,不能使用在宏中. C99编译器标准终于改变了这种局面,它允许你可以定义可变参数宏(var