lambda方法引用獲取欄位屬性

Ruthless發表於2022-08-15

1、IGetter

import java.io.Serializable;

@FunctionalInterface
public interface IGetter<T> extends Serializable {
    Object get(T source);
}

 

2、ISetter

import java.io.Serializable;

@FunctionalInterface
public interface ISetter<T, U> extends Serializable {
    void set(T t, U u);
}

 

3、NjBeanUtils

import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * @author : whp
 * @describe :
 * @date : 2022/6/29 9:53
 */
@Slf4j
public class NjBeanUtils extends BeanUtil {
    /**
     * list 列表之間的複製
     * @param sources
     * @param target
     * @return
     * @param <S>
     * @param <T>
     */
    public static <S, T> List<T> copyListProperties(List<S> sources, Supplier<T> target) {
        if(CollUtil.isEmpty(sources)){
            return null;
        }
        return copyListProperties(sources, target, null);
    }

    /**
     * @author whp
     * 使用場景:Entity、Bo、Vo層資料的複製,因為BeanUtils.copyProperties只能給目標物件的屬性賦值,卻不能在List集合下迴圈賦值,因此新增該方法
     * 如:List<AdminEntity> 賦值到 List<AdminVo> ,List<AdminVo>中的 AdminVo 屬性都會被賦予到值
     * S: 資料來源類 ,T: 目標類::new(eg: AdminVo::new)
     */
    public static <S, T> List<T> copyListProperties(List<S> sources, Supplier<T> target, NjBeanUtilsCallBack<S, T> callBack) {
        List<T> list = new ArrayList<>(sources.size());
        for (S source : sources) {
            T t = target.get();
            copyProperties(source, t);
            list.add(t);
            if (callBack != null) {
                // 回撥
                callBack.callBack(source, t);
            }
        }
        return list;
    }
    
    /**
     * 透過getter的方法引用獲取欄位名
     */
    public static <T> String convertToFieldName(IGetter<T> fn) {
        try {
            SerializedLambda lambda = getSerializedLambda(fn);
            String methodName = lambda.getImplMethodName();
            String prefix = null;
            if (methodName.startsWith("get")) {
                prefix = "get";
            } else if (methodName.startsWith("is")) {
                prefix = "is";
            }
            if (prefix == null) {
                log.error("無效的getter方法: " + methodName);
            }

            return toLowerCaseFirstOne(methodName.replace(prefix, ""));
        } catch (Exception e) {
            log.error("透過getter的方法引用獲取欄位名失敗", e);
            return null;
        }
    }

    /**
     * 透過setter的方法引用獲取欄位名
     * @throws Exception 
     */
    public static <T, U> String convertToFieldName(ISetter<T, U> fn) {
        try {
            SerializedLambda lambda = getSerializedLambda(fn);
            String methodName = lambda.getImplMethodName();
            if (!methodName.startsWith("set")) {
                log.error("無效的setter方法:" + methodName);
            }
            return toLowerCaseFirstOne(methodName.replace("set", ""));
        } catch (Exception e) {
            log.error("透過setter的方法引用獲取欄位名失敗", e);
            return null;
        }

    }

   /**
    * 關鍵在於這個方法
    */
    private static SerializedLambda getSerializedLambda(Serializable fn) throws Exception {
        Method method = fn.getClass().getDeclaredMethod("writeReplace");
        method.setAccessible(Boolean.TRUE);
        SerializedLambda lambda = (SerializedLambda) method.invoke(fn);
        return lambda;
    }

    /**
     * 字串首字母轉小寫
     */
    private static String toLowerCaseFirstOne(String field) {
        if (Character.isLowerCase(field.charAt(0)))
            return field;
        else {
            char firstOne = Character.toLowerCase(field.charAt(0));
            String other = field.substring(1);
            return new StringBuilder().append(firstOne).append(other).toString();
        }
    }
}

 

4、單元測試

    public static void main(String[] args) {
         System.out.println(NjBeanUtils.convertToFieldName(ShopIndex::getAddress));
         System.out.println(NjBeanUtils.convertToFieldName(ShopIndex::getCityId));
         System.out.println(NjBeanUtils.convertToFieldName(ShopIndex::setMartId));
         System.out.println(NjBeanUtils.convertToFieldName(ShopIndex::setMartLocation));
    }

5、執行結果

address
cityId
martId
martLocation

 

相關文章