Java泛型的局限和使用经验泛型的局限泛型的常用经验参考资料

本文首发于个人网站:Java泛型的局限和使用经验

这篇文章主要总结泛型的一些局限和实际的使用经验

泛型的局限

  1. 任何基本类型不能作为类型参数 经过类型擦除后,List<Integer>中包含的实际上还是Object的域,而在Java类型系统中Object和基本类型是两套体系,需要通过“自动装包、拆包机制”来进行交互。 public class ListOfInt { public static void main(String[] args) { //(1)通过自动装包和拆包,在泛型中和基本类型进行交互 List<Integer> li = new ArrayList<>(); for (int i = 0; i < 5; i++) { //发生自动装包 li.add(i); } //发生自动拆包 for (int i: li) { System.out.println(i); } //(2)错误示例 List<int> list = new ArrayList<>(); } } ​
  2. 任何在运行时需要知道确切类型信息的操作都无法工作。 由于Java的泛型是编译期泛型(在进入运行时后没有泛型的概念),因此运行时的类型转换和类型判定等操作都没有效果。 public class Erased<T> { public void f(Object[] args) { //(1)不能在类型参数上使用instanceof操作, instanceof右边必须是某个原生类型或数组 if (args instanceof T) {} //(2)不能直接实例化类型参数 T var = new T(); //(3)不能这么定义泛型数组,原因同上 T[] array = new T[100]; //(4)先定义一个Object数组,再强转成T[]数组,绕过泛型检查,但是会收到一个告警 T[] array2 = (T[])new Object[100]; } } ​
  3. 冲突1:方法名一样,参数列表是同一个类型参数的两个泛型方法,重载将产生相同的函数签名; package org.java.learn.generics; import java.util.List; public class UserList<W, T> { void f(List<T> v) {} void f(List<W> v) {} } 从图中的提示可以看出,在泛型擦除后,这两个方法签名完全相同,产生冲突;

泛型导致的重载冲突

  1. 冲突2:使用泛型接口时,需要避免重复实现同一个接口 interface Payable<T> {} class Employee implements Payable<Employee> {} class Hourly extends Employee implements Payable<Hourly> {} IDEA编辑器给出如下所示——“Payable不能被不同的类型参数继承,即不能重复实现同一个接口”

使用泛型接口时的冲突 ​

  1. 不能在静态域或方法中引用类型参数 public class Erased<T> { public static void f(Object[] args) { //不能在静态域中引用类型参数 if (args instanceof T) {} T var = new T(); T[] array = new T[100]; T[] array2 = (T[])new Object[100]; } } 这个例子跟问题2基本相同,唯一是在方法的签名里多了一个static关键字,然后引发编译错误的原因就变成了:在静态域中无法引用类型变量(入下图所示)。

2017-12-0920.31.09.png

泛型的常用经验

  1. 尽量消除异常,初学者容易写出使用原生类型的代码,或者使用泛型不当的代码,现在编辑器非常先进,尽量消除提示的异常;对于开发者自己确认不需要消除切可以工作的代码,可以使用@SuppressWarnings("unchecked")屏蔽掉异常;
  2. 能用泛型类(或接口)的时候尽量使用;能用泛型方法的时候尽量使用泛型方法;
  3. 定义API时,尽量使用泛型; public class PlainResult<T> extends BaseResult { private static final long serialVersionUID = -xxxxxx; private T data; public PlainResult() { } public T getData() { return this.data; } public void setData(T data) { this.data = data; } } ​
  4. 编写基础工具类时,尽量使用泛型;
    • 例子1:通用的返回值对象

    //使用泛型类 @Data @Builder @AllArgsConstructor @NoArgsConstructor public class DataListPageInfo<T> { int page; int pageSize; int totalCount; List<T> items = new ArrayList<>(); }

    • 例子2:缓存操作工具类,这里使用了

    @Component public class RedisTemplateService { private static final Logger LOGGER = LoggerFactory.getLogger(RedisTemplateService.class); @Resource private RedisTemplate redisTemplate; private String getKey(String key, Object... params) { if (params == null || params.length <= 0) { return key; } return String.format(key, params); } //这里使用了泛型方法 public <T> T getFromJsonCache(Class<T> type, String keyPrefix, Object... params) { try { String ret = getString(keyPrefix, params); return JSON.parseObject(ret, type); } catch (Exception e) { LOGGER.error("json parse error, type={},keyPrefix={},params={}", type, keyPrefix, params, e); } return null; } …… } ​

参考资料

  1. 《Java编程思想》
  2. 《Java核心技术》
  3. 《Effective Java》

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏开发与安全

C++中四种类型转换以及const_cast是否能改变常量的问题

we have four specific casting operators:dynamic_cast, reinterpret_cast, static_c...

228100
来自专栏码云1024

python简明笔记

通过 for 语句我们可以使用 for 循环。Python 里的 for 循环与 C 语言中的不同。这里的 for 循环遍历任何序列(比如列表和字符串)中的每一...

63390
来自专栏LanceToBigData

JavaSE(十一)之异常处理详解

一、异常概述   在我们日常生活中,有时会出现各种各样的异常,例如:职工小王开车去上班,在正常情况下,小王会准时到达单位。但是天有不测风云,在小王去上班时,可能...

20290
来自专栏小灰灰

Java学习之深拷贝浅拷贝及对象拷贝的两种方式

I. Java之Clone 0. 背景 对象拷贝,是一个非常基础的内容了,为什么会单独的把这个领出来讲解,主要是先前遇到了一个非常有意思的场景 有一个任务,需要...

42290
来自专栏C/C++基础

C++inline函数简介

inline函数是由inline关键字来定义,引入inline函数的主要原因是用它替代C中复杂易错不易维护的宏函数。

25220
来自专栏Java编程

Java异常的深入研究与分析

本文是异常内容的集大成者,力求全面,深入的异常知识研究与分析。本文由金丝燕网独家撰写,参考众多网上资源,经过内容辨别取舍,文字格式校验等步骤编辑而成,以飨读者。...

39000
来自专栏用户2442861的专栏

Java中Synchronized的用法

原文:http://blog.csdn.net/luoweifu/article/details/46613015 作者:luoweifu 转载请标名...

8410
来自专栏编程坑太多

理解 JavaScript 的 async/await

22230
来自专栏JAVA高级架构

Java面试题合集

1.抽象类与接口的区别是什么? 一个类可以实现多个接口,但是只能继承以及抽象类。类如果要实现一个接口,它必须要实现接口声明的所有方法。但是,类可以不实现抽象类声...

384100
来自专栏陈树义

JVM系列第7讲:JVM 类加载机制

当 Java 虚拟机将 Java 源码编译为字节码之后,虚拟机便可以将字节码读取进内存,从而进行解析、运行等整个过程,这个过程我们叫:Java 虚拟机的类加载机...

31720

扫码关注云+社区

领取腾讯云代金券