说到Java泛型,大家应该都不陌生。常用于限制Java各种容器的数据类型。它是Java JDK 1.5后增加的特性,所以已经是一个比较老的特性了。这里写这篇文章主要目的是为了给大家复习泛型的知识点。
Java泛型(Generic) 介绍泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。
作用Java泛型的主要作用有:
- 类型安全:编译时强类型检查,编译器可以直接验证类型假设。
- 消除强制类型转换:消除代码中的许多强制类型转换,使得代码可读性更高,减少出错机会。
- 提高代码复用性:泛型可以避免一些有相同特性且重复的代码。
至于泛型是否会带来性能问题,这个说法不一。但泛型是一个编译时的功能,增强代码可读性、复用性的收益远大于泛型进行类型转换的影响。
应用场景 泛型类package com.hust.zhang.generic; import lombok.AllArgsConstructor; import lombok.Getter; @AllArgsConstructor public class GenericClass{ @Getter private T item; }
定义一个Java泛型类,泛型类中的成员变量的类型可以通过类对象实例化时进行约束,测试如下,
public class GenericTest {
public static void main(String[] args) {
GenericClass generic1 = new GenericClass<>("item");
GenericClass generic2 = new GenericClass<>(123L);
System.out.println(generic1.getItem().getClass().getName());//java.lang.String
System.out.println(generic2.getItem().getClass().getName());//java.lang.Long
}
}
泛型接口
Java泛型接口和泛型类基本类似,用过Mybatis或者Mybaits-plus的同学应该都知道使用代码自动生成工具生成的Mapper文件里baseMapper就是一个泛型接口,我拿出来一个作为示例。
package com.baomidou.mybatisplus.core.mapper; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import java.io.Serializable; import java.util.Collection; import java.util.List; import java.util.Map; import org.apache.ibatis.annotations.Param; public interface baseMapperextends Mapper { int insert(T entity); int deleteById(Serializable id); int deleteByMap(@Param("cm") Map columnMap); int delete(@Param("ew") Wrapper wrapper); int deleteBatchIds(@Param("coll") Collection extends Serializable> idList); int updateById(@Param("et") T entity); int update(@Param("et") T entity, @Param("ew") Wrapper updateWrapper); T selectById(Serializable id); List selectBatchIds(@Param("coll") Collection extends Serializable> idList); List selectByMap(@Param("cm") Map columnMap); T selectOne(@Param("ew") Wrapper queryWrapper); Integer selectCount(@Param("ew") Wrapper queryWrapper); List selectList(@Param("ew") Wrapper queryWrapper); List
然后数据库表的接口继承baseMapper,各个数据库表接口的实现继承ServiceImpl类就具备了增删改查的基本功能了。
public class ServiceImpl泛型方法, T> implements IService { protected Log log = LogFactory.getLog(getClass()); @Autowired protected M baseMapper; @Override public M getbaseMapper() { return baseMapper; } //... 省略 ... }
上面的栗子里其实包含了泛型类、泛型接口和泛型方法,下面再举一个单纯的泛型方法,直接打印泛型的数据类型。
private static实际应用void printGeneric(T t){ System.out.println(t.getClass().getName()); }
下面看一个实际应用,比如以太专线和以太专网的公共属性类为以太连接。
以太连接父类:
@Data
public class EthConnection {
private String attribute;
}
以太专线子类:
@Data
public class ELineConnection extends EthConnection{
private String uuid;
}
以太专网子类:
@Data
public class ELanConnection extends EthConnection{
private String uuid;
}
比如我想对以太专线和以太专网子类传入它们共有的属性,那么我得像下面这么做,
public class GenericDemo {
public static void main(String[] args) {
String attr = "commonAttribute";
List eLanConnectionList = buildELanConnection(attr);
List eLineConnectionList = buildELineConnection(attr);
eLanConnectionList.stream().forEach(System.out::println);
eLineConnectionList.stream().forEach(System.out::println);
}
private static List buildELanConnection(String attr) {
ELanConnection connection = new ELanConnection();
connection.setAttribute(attr);
return Lists.newArrayList(connection);
}
private static List buildELineConnection(String attr) {
ELineConnection connection = new ELineConnection();
connection.setAttribute(attr);
return Lists.newArrayList(connection);
}
}
这里共有属性不多,感觉没啥,但如果它们需要传入的共有属性很多且都相同,那么重复的代码就太多了。设置共同属性的代码段可以抽取出来,如果属性不多,我下面这种做法也许显得很多余。这样设计是否合适大家可以一起品一品。
public class GenericDemo {
private static final int ELAN_TAG = 1;
private static final int ELINE_TAG = 2;
public static void main(String[] args) {
String attr = "commonAttribute";
List eLanConnectionList = buildELanConnection(attr);
List eLineConnectionList = buildELineConnection(attr);
eLanConnectionList.stream().forEach(System.out::println);
eLineConnectionList.stream().forEach(System.out::println);
}
private static List buildELanConnection(String attr) {
return castELanConnection(buildConnection(attr, ELAN_TAG));
}
private static List buildELineConnection(String attr) {
return castELineConnection(buildConnection(attr, ELINE_TAG));
}
private static List castELanConnection(List extends EthConnection> connections) {
if (CollectionUtils.isEmpty(connections)){
return Lists.newArrayList();
}
return connections.stream().map(ELanConnection.class::cast).collect(Collectors.toList());
}
private static List castELineConnection(List extends EthConnection> connections) {
if (CollectionUtils.isEmpty(connections)){
return Lists.newArrayList();
}
return connections.stream().map(ELineConnection.class::cast).collect(Collectors.toList());
}
private static List extends EthConnection> buildConnection(String attr, int tag) {
if (tag == ELINE_TAG) {
ELineConnection connection = new ELineConnection();
addAttribute(attr, connection);
return Lists.newArrayList(connection);
} else if (tag == ELAN_TAG) {
ELanConnection connection = new ELanConnection();
addAttribute(attr, connection);
return Lists.newArrayList(connection);
}
return Lists.newArrayList();
}
private static void addAttribute(String attr, EthConnection connection) {
connection.setAttribute(attr);
}
}
C++模板(Template)
c++模板和Java泛型类似,它叫模板。以一或多个模版形参参数化,形参有以下三种:
- 类型模板形参。
- 非类型模板形参。
- 模板模板形参。
模板定义的实体有:类模板、函数模板、别名模板(C++11起)、变量模板(C++14起)
详细内容参看文末参考链接。
参考链接:
1、Java 泛型 | 菜鸟教程
2、模板 - cppreference.com



