T和Function 的使用,是一名JAVA工程师通向JAVA架构师的必经之路。
若你在研究源码和别人的代码里发现了 T 和Function ,觉得不知所云,那么本篇博客,将带你掌握他们。
学会写泛型工具函数,让代码可读性更强、复用性强更雅 。
用JDK 1.8 以下的可以走开了,不用看了。
我有一个如下代码的类,我想打印一下某个变量的值,但是有时候想打印 name1 ,有时候想打印 namename4 ,
有时候想打印 name3 ...等等。按照传统的方式需要写4函数才能完成需求吧。这也太烦了,怎么能省事呢?
public class Test {
private String name1;
private String name2;
private String name3;
private String name4;
public String getName1() {
return name1;
}
public void setName1(String name1) {
this.name1 = name1;
}
public String getName2() {
return name2;
}
public void setName2(String name2) {
this.name2 = name2;
}
public String getName3() {
return name3;
}
public void setName3(String name3) {
this.name3 = name3;
}
public String getName4() {
return name4;
}
public void setName4(String name4) {
this.name4 = name4;
}
}
反射很多人都会,但感觉有点烦,通过传进去的字符串找到属性然后打印,不推荐。
public static void main(String[] args) {
Test test = new Test();
test.setName1("user1");
test.setName2("user2");
test.setName3("user3");
test.setName4("user4");
printName(test,"name1");
}
private static void printName(Test test, String name1) {
Class extends Test> tClass = test.getClass();
Field[] field = tClass.getDeclaredFields();
for (int i = 0; i < field.length; i++) {
Field f = field[i];
//设置可以访问私有变量
f.setAccessible(true);
if(f.getName().equals(name1)){
try{
PropertyDescriptor pd = new PropertyDescriptor(f.getName(), tClass);
Method readM = pd.getReadMethod();//获得读方法
String string = (String)readM.invoke(test);
System.out.println(string);
}catch (Exception e){
System.out.println(e.getMessage());
}
}
}
}
在《Java常用的工具类 》里有提到,是 Apache Commons 下的 BeanUtils。
看这也不错,但是这个别人的东西。需要去管理jar包版本吧。引了一个包,可能带了一会我不需要的。
还有就是 ,传入一个字符串作为参数,来对应,总是不好的,万一对应不上会报错的,编译并不能帮我们检查出错误。
main函数不变稍作改造。
import org.apache.commons.beanutils.BeanUtils;
private static void printName(Test test, String name1) {
try{
String string = BeanUtils.getProperty(test, name1);
System.out.println(string);
}catch (Exception e){
System.out.println(e.getMessage());
}
}
我们不在以属性名作为入参,而是采用泛型函数的形式,把属性对应的get方法作为入参。
代码如下:
public static void main(String[] args) {
Test test = new Test();
test.setName1("user1");
test.setName2("user2");
test.setName3("user3");
test.setName4("user4");
printName(test,Test::getName1);
}
private static void printName(Test test, Function getName1) {
String apply = getName1.apply(test);
System.out.println(apply);
}
这种方式不需要处理异常,若是根本没有getName1() 则编译都过不了。
编译器好的话,写的时候,打上 "Test::" 之后直接提示可选的函数名,是非常好的编码体验。
Function
我们还不想局限在Test这个类,想写成通用的类,还不想局限字符串类型的属性打印。
总之 ,想任何类的任意属性都可以调用这个方法。
我们只需要扩展我们的函数即可,这就需要用到 T 泛型了。
改造如下:
public static void main(String[] args) {
Test test = new Test();
test.setName1("user1");
test.setName2("user2");
test.setName3("user3");
test.setName4("user4");
printName(test,Test::getName2);
}
private static void printName(T test, Function getName1) {
Object object = getName1.apply(test);
if(object != null){
String apply = object.toString();
System.out.println(apply);
}
}
至此,教学部分结束。
我知道python里有个list列表随机取一个的方法。我用java实现一个。
public static T randomChoice(List data) {
//随机取一个工具函数,不理解泛型的,把T换成String
int num = (int) (Math.random() * data.size());
return data.get(num);
}
————————————————
版权声明:本文为CSDN博主「wang_lianjie」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wang_lianjie/article/details/103194523
这段代码出自我的另一篇博客,讲文章生成器的,中间一处使用过。
list列表分发,就是把长list切换成多个小list。
主要用在sql拼 in () 里的东西时,上限1000个,不得不切分后调用。
还有,很多数据需要多线程处理时,100多万数据,分100个线程分别处理时,先切分好了再分别处理(这个有机会单开个博客写)。
public static List> divSublist(List originalList, int len) {
List> answer = new ArrayList();
int size = originalList.size();
int count = (size + len - 1) / len;
for(int i = 0; i < count; ++i) {
answer.add(originalList.subList(i * len, (i + 1) * len > size ? size : len * (i + 1)));
}
return answer;
}
是一个后端面临很普遍的问题,listToTree,list表变tree格式。
他有个前提就是 : list里的对象需要有三个东西 : 本身的ID ,父级的ParentID , 以及一个子集list 列表。
工具函数如下: 觉得有用点个赞支持一下
public static List toTree(List data,
Function getIdFunction,
Function getParentIdFunction,
BiConsumer> addChildFunction) {
if(data == null){
throw new ListToTreeException("树目标节点为空");
}
List result = new ArrayList<>();
Map> tmpMap = new HashMap<>();
//按“parentId值”分组
for (T record : data) {
Object parentIdValue = getParentIdFunction.apply(record);
if (parentIdValue == null) {
result.add(record);
} else {
String parentId = String.valueOf(parentIdValue);
if (!tmpMap.containsKey(parentId)) {
tmpMap.put(parentId, new ArrayList<>());
}
tmpMap.get(parentId).add(record);
}
}
//正式处理
for (T record : data) {
String idValue = String.valueOf(getIdFunction.apply(record));
if (tmpMap.containsKey(idValue)) {
addChildFunction.accept(record, tmpMap.get(idValue));
tmpMap.remove(idValue);
}
}
if (tmpMap.size() > 0) {
throw new ListToTreeException("以下节点,存在上下级断档,无法正常显示[" + tmpMap + "]");
}
return result;
}
调用时代码如下,大家可以试一试。
TreeUtil.toTree(answer,
CodeBean::getCode,
CodeBean::getParentCode,
CodeBean::setChildren);
注意: BiConsumer
list转map,我用过很多次 。
用途很多,我在 《Map格式化 》里,提到过好处,可以解决很多哈希表问题。
实际工作中,需要转码啊,java代码做对应啊,map格式化频繁使用。
/**
* 提取list的两个属性,组成map
* 默认不覆盖
* @param data
* @param getKeyFunction
* @param getValueFunction
* @param
* @return
*/
public static Map toMap(List data, Function getKeyFunction, Function getValueFunction) {
Map res = new HashMap<>();
if(data != null && !data.isEmpty()){
for(T item : data){
if(getKeyFunction.apply(item)==null||getValueFunction.apply(item)==null){
continue;
}
String key = getKeyFunction.apply(item).toString();
String value = getValueFunction.apply(item).toString();
if (!res.containsKey(key)){
res.put(key,value);
}
}
}
return res;
}
欢迎各位斧正,提出异议、建议、以及技术问题。