泛型方法、Function类的函数化编程与调用

0、引言

        在项目开发的过程中,常常需要将一些高频复用的方法封装成工具类,例如最近学到的Redis缓存中,解决缓存穿透、解决缓存击穿的方法(例如解决缓存穿透的问题的方法queryWithPassThrough),传入一个Long型id,返回你要查询的数据。

        问题在于,queryWithPassThrough方法不能指定一种返回类型,比如店铺类型、人员类型、评论类型等等各种类型,作为工具类,我们希望它能够随着调用而返回对应的类型

        因此,需要使用泛型方法来完成这个需求。

1、泛型方法的区分与定义

定义:

        在确定某个方法是否为泛型方法时,需要判断其是否声明了类型参数,类型参数的作用域是在方法内还是在整个泛型类中。

        如果只涉及到特定方法操作的类型参数,则该方法是泛型方法;如果类型参数定义在泛型类中,则不论它们是否被方法使用,该方法都不是泛型方法。

举例:        

        例如, 按照上述原则,用以下例子method0、1、2进行说明:       

class a{
    T t;
    M m;

    public void method0(T t,M m){
        //不是泛型方法,仅仅是用了泛型类的标识符
        System.out.println(t.getClass());
        System.out.println(m.getClass());
        System.out.println("====================");
    }
    publicvoid method1(S s,U u){
        System.out.println(s.getClass());
        System.out.println(u.getClass());
        System.out.println("====================");
    }//是泛型方法,根据传入的参数自己规划数据类型

    publicvoid method2(K k,M m){
        //k来自于泛型方法,而m是泛型类的标识符,但因为有K
        //因此也是泛型方法
        System.out.println(k.getClass());
        System.out.println(m.getClass());
    }
}
  1. method0 方法的类型参数 T 和 M 都是定义在实例变量上的泛型类型,它们只在泛型类中使用,而不涉及方法的特定操作。因此,method0 不是泛型方法。

  2. method1 方法声明了一个类型参数 S 和一个类型参数 U,这些参数只在该方法中使用。由于它们是方法级别的,所以 method1 是泛型方法。

  3. method2 方法声明了一个类型参数 K,它只在该方法中使用,所以 method2 是泛型方法。但是,方法中的参数 M 是泛型类的类型参数,因此也可以用在泛型方法中。

2、使用泛型方法实现自适应返回类型

 public  R queryWithPassThrough(
String keyPrefix, ID id, Class type, 
Function dbFallback, 
Long time, TimeUnit unit)

        例如上述方法,是防止缓存穿透的方法定义,是定义的两个泛型,R类型是这个方法的返回类型(即实际要查询的数据类型),通过函数式编程Function函数,来实现查询ID类型的id所对应的数据类型R的数据。

        这里将介绍一下:(1)Function函数的使用。(2)用反射实现返回指定类型数据

2.1 Function函数的使用

        由于具体的查询语句可能是变化的,因此,“具体怎么查”,这个查询函数不是定死的,作为工具类也不能把它写死,而是采用函数式编程的方法:
        即,要用什么函数查询,就传入什么函数。

Funtion的使用方法与底层原理:

        从源码看,对于Function接口,其内部存在一个apply方法:

泛型方法、Function类的函数化编程与调用_第1张图片

        对于第二章开头的代码,内部调用:     

        R r = dbFallback.apply(id);

        在本例中:applyFunction 接口中定义的抽象方法,用于接受一个参数并返回一个结果。在 Function 中,apply 方法接受一个类型为 ID 的参数,返回一个类型为 R 的结果。

        因此,我们只需要重写这个apply方法,变成我们想要使用的方法即可!

        在以上的基础上如果想要调用函数,可以使用lambda表达式,传入id->getById(id),则相当于:

Function dbFallback = id -> getById(id);
//或者:
Function dbFallback = this::getById;

//可能有的人对lambda语法不熟悉,其实就是使用了匿名内部类的方式实现apply方法:
//即:
Function dbFallback = new Function<>(){
    @Override
    public R apply(ID id){
        return getById(id);
    }
}

  在调用时的语句就可以这么写:      

Shop shop = cacheClient
        .queryWithPassThrough(CACHE_SHOP_KEY, id, Shop.class, id->getById(id), CACHE_SHOP_TTL, TimeUnit.MINUTES);

        在实际上,我们想传入什么方法来进行查询都可以(想怎么实现apply都可以),从而实现了函数式编程。

2.2 用反射实现返回类型的变化

 public  R queryWithPassThrough(String keyPrefix, ID id, Class type, 
Function dbFallback, Long time, TimeUnit unit)

        在以上的方法定义下,编写具体查询代码的时候发现:从Redis里get得到的数据时json字符串格式的,但是我们想要的是任意的“R”型,比如商店类的Shop类型,等类型,该怎么办呢?

        String key = keyPrefix + id;
        // 1.从redis查询商铺缓存
        String json = stringRedisTemplate.opsForValue().get(key);
        // 2.判断是否存在
        if (StrUtil.isNotBlank(json)) {
            // 3.存在,直接返回
            return JSONUtil.toBean(json, type);
        }

        这里可以采用反射的方法:使用JSONUtil类中的toBean方法:

        传入:Shop.class即可,就会通过这个toBean进行转换。

你可能感兴趣的:(缓存,数据库,sql,redis,java)