人生靠反省,Java靠泛型

Posted 一猿小讲

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了人生靠反省,Java靠泛型相关的知识,希望对你有一定的参考价值。

昨天有同事问 UserService、XxxService 都会调用 Dao 的 insert、update ... ...,这些重复的代码,有没有办法变得灵活一些?

 

巧了,和咱们分享的主题刚好碰上,卖个关子,先不谈解决方案,就当啥事没有发生,重新引入今天的话题(捂嘴笑)。

 

想蜕变的研发人员,偶尔会品味一下 Java 的源码;久经职场的码农,时不时也会搭建一下项目架构。其实无论你是刚入猿门,还是骨灰级战神,今天的分享你多多少少都听过、关注过、迷茫过甚至用过。

 

好了,准备好小板凳,让我们一起聊聊,在你看源码、搭架构过程中都躲避不开的 Java 中那些 E、T、?等字母都是啥意思?

 

先科普一下知识,什么是泛型?聊啥概念,直接上代码,直奔主题,先从 JDK 1.8 摘点源码出来,一起与泛型打个照面,混个脸熟。

 

1. 

啥是 E?

 

E 可以说在 JDK 源码中无处不在,咱们就从 ArrayList 的源码进行聊起。

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    
    /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    // 更多代码 ... ...
}

  

这个 E 在此处用来创建与初始 ArrayList 的类型,其实我们可以把它换成实际的类型,举个栗子:

 

640?wx_fmt=png

 

发现编译器会把 E 被真正的类型所取代,其实也就是当我们创建出 ArrayList<String>,那么对应的 add() 会变成 add(String e);当我们创建出 ArrayList<Dog>,那么对应的 add() 会变成 add(Dog e)。

 

但是往往一不留神你会这么写,其实你也不想这么写,程序猿心里的苦,其实说也说不清楚(捂嘴笑)。

 

640?wx_fmt=png

 

这就说明了为什么写代码的时候,老是经常编译不过去,老是有警告,谁让咱定义的是 String 类型,而咱们又非要往集合中放入一条狗呢。

 

来自灵魂的拷问:只能用“E”来表示吗?身边同事还真问过我这个问题,在这我还是再认真的回答一次

 

其实我们可以使用任何合法的 Java 标识字符串,但是大家用单一的字母来表示,已经成为一种习惯,而 E 又代表 Element 元素的意思,所以在集合中经常用 E 来表示,但是源码中大概率会碰到字母 T。

 

2. 

啥是T?

public static <T extends Comparable<? super T>> void sort(List<T> list) {
    list.sort(null);
}

  

上面代码摘自 JDK 1.8 Collections 的源码,我们发现了“T”,而且还有“?”,问号咱们暂且放一边,着重聊一聊“T”。

 

分两部分去看,其中第一部分 <T extends Comparable> 指的是 T 必须是 Comparable 的实现(这是重点、这是重点、这是重点);第二部分方法入参 sort(List<T> list),指的是仅能传入继承 Comparable 的参数化类型的 list。

 

其实也很难理解,不妨再摘个栗子配合理解。

 

640?wx_fmt=png

 

从 JDK 1.8 源码中摘出 String 的源码,把 String 代入 Collections 的 sort 方法,替换为 T 尝试悟一下,看看是否 ok?!变量替换数学中大家都学过,就不深入了。

 

640?wx_fmt=png

 

但是你实际开发中,有没有遇到过上图的情形,在进行狗狗列表排序时,就死活报错!报错!!原因就是因为要排序的狗狗,必须要实现 Comparable,方能进行排序。

 

好了,T 就不再聊了,咱们还是说说这个问号吧。

 

3.

“?”问号是啥?

 

问号,看到这个估计会一脸懵逼,其实就是未知,代表一万种可能性,在 Java 中就是万用字符。

 

640?wx_fmt=png

 

那我们再看看上面摘自 JDK 1.8 Collections 的源码,那么 Comparable<? super T> 则代表 Comparable 的类型参数必须是 T 或 T 的父型,你可能有迷糊了,还是再抛点代码吧。

 

640?wx_fmt=png

 

看到效果了没,因为要针对狗狗排序,排序的类型必须是 Dog 或者是 Dog 的父类型,咱们传入 String 类型,当然是编译不通过啦,不妨改成 Dog 或者 Object 自己试一下,看看效果,在此不做演示。不过结论还是要说一下:Comparable<? super T> 则代表 Comparable 的类型参数必须是 T 或 T 的父类型。

 

提到 <? super T>,那不得不再提一提 <? extends T>,其实只要上面的搞明白了,这个也就非常清晰了,问号代表继承和实现 T,栗子就不抛了,在框架源码中遇到知道是啥意思就行了。

 

4.

任性的总结。

 

其实泛型是编译期的一种检查,能够有效防止狗入人海,其中主要分为使用泛型的类以及使用泛型的方法;其中 E 主要用于集合的元素,除了 E 之外绝大部分是 T,然后 Java 还引入了一种万用字符是问号,不过可以用任意 Java 有效标识符进行表示,不要再纠结、不要再纠结、不要再纠结。

 

说了这么多,咱们开篇的问题还没有解决啊?话不多说,直接抛代码,不懂也没关系,注意理解上面几个字母就行了,下面这段代码分享给需要的朋友(哎呦我去,又出来个字母 D)。

@Transactional(readOnly = true)
public abstract class CrudService<D extends CrudDao<T>, T extends DataEntity<T>> extends BaseService {
   
   /**
    * 持久层对象
    */
   @Autowired
   protected D dao;
   
   /**
    * 获取单条数据
    * @param entity
    * @return
    */
   public T get(T entity) {
      return dao.get(entity);
   }
   
   /**
    * 查询列表数据
    * @param entity
    * @return
    */
   public List<T> findList(T entity) {
      return dao.findList(entity);
   }
   
   /**
    * 查询分页数据
    * @param page 分页对象
    * @param entity
    * @return
    */
   public Page<T> findPage(Page<T> page, T entity) {
      entity.setPage(page);
      page.setList(dao.findList(entity));
      return page;
   }

   /**
    * 保存数据(插入或更新)
    * @param entity
    */
   @Transactional(readOnly = false)
   public int save(T entity) {
      if (entity.getIsNewRecord()){
         entity.preInsert();
         return dao.insert(entity);
      }else{
         entity.preUpdate();
         return dao.update(entity);
      }
   }
   
   /**
    * 删除数据
    * @param entity
    */
   @Transactional(readOnly = false)
   public int delete(T entity) {
      return dao.delete(entity);
   }
}

 

5.

好了,今天分享就到这儿吧,希望能够解你所惑;希望能在你前进的道路上,帮你披荆斩棘。如果感觉有点帮助,欢迎在看、秒赞,疯狂分享转发,因为你的每一次分享,我都认真当成了鼓励与喜欢。

 

 

以上是关于人生靠反省,Java靠泛型的主要内容,如果未能解决你的问题,请参考以下文章

java--泛型--泛型接口&泛型方法

JAVA泛型知识点

操作 Java 泛型:泛型在继承方面体现与通配符使用

小学生都会的Python,你该反省反省学学了!

2021-深信服-安服实习-面试反省

Java泛型:类型擦除