lambdametafactory装箱/取消装箱参数和返回类型

r1zk6ea1  于 2021-08-20  发布在  Java
关注(0)|答案(2)|浏览(509)

我有以下两种方法:

  1. public static <T, R> IGetter<T, R> createGetterViaMethodname( final Class<T> clazz, final String methodName,
  2. final Class<R> fieldType )
  3. throws Throwable
  4. {
  5. final MethodHandles.Lookup caller = MethodHandles.lookup();
  6. final MethodType methodType = MethodType.methodType( fieldType );
  7. final MethodHandle target = caller.findVirtual( clazz, methodName, methodType );
  8. final MethodType type = target.type();
  9. final CallSite site = LambdaMetafactory.metafactory(
  10. caller,
  11. "get",
  12. MethodType.methodType( IGetter.class ),
  13. type.erase(),
  14. target,
  15. type );
  16. final MethodHandle factory = site.getTarget();
  17. return (IGetter<T, R>) factory.invoke();
  18. }
  19. public static <T, I> ISetter<T, I> createSetterViaMethodname( final Class<T> clazz, final String methodName,
  20. final Class<I> fieldType )
  21. throws Throwable
  22. {
  23. final MethodHandles.Lookup caller = MethodHandles.lookup();
  24. final MethodType methodType = MethodType.methodType( void.class, fieldType );
  25. final MethodHandle target = caller.findVirtual( clazz, methodName, methodType );
  26. final MethodType type = target.type();
  27. final CallSite site = LambdaMetafactory.metafactory(
  28. caller,
  29. "set",
  30. MethodType.methodType( ISetter.class ),
  31. type.erase(),
  32. target,
  33. type );
  34. final MethodHandle factory = site.getTarget();
  35. return (ISetter<T, I>) factory.invoke();
  36. }

包括以下两个接口:

  1. @FunctionalInterface
  2. public interface IGetter<T, R>
  3. {
  4. @Nullable
  5. R get( T object );
  6. }
  7. @FunctionalInterface
  8. public interface ISetter<T, I>
  9. {
  10. void set( T object, @Nullable I value );
  11. }

这适用于所有类类型,包括基本类型的数字 Package 器,例如 Integer 对于 int . 然而,如果我有一个二传手 int 或者返回 ìnt ,它尝试传递/返回数字 Package ,导致异常。
在不必使用其他方法的情况下,装箱/取消装箱的正确方法是什么。这里的原因是为了保持api干净且易于使用。我愿意在这里为拳击/拆箱表演一次小小的打击。

5t7ly7z5

5t7ly7z51#

没有内置的“漂亮”方法将基本类转换为 Package 类,因此必须使用如下Map:

  1. private final static Map<Class<?>, Class<?>> map = new HashMap<>();
  2. static {
  3. map.put(boolean.class, Boolean.class);
  4. map.put(byte.class, Byte.class);
  5. map.put(short.class, Short.class);
  6. map.put(char.class, Character.class);
  7. map.put(int.class, Integer.class);
  8. map.put(long.class, Long.class);
  9. map.put(float.class, Float.class);
  10. map.put(double.class, Double.class);
  11. }

或者在这里使用其他方法之一。
一旦你这么做了,你可以检查一下 fieldType 是原始的。如果是,请通过在Map中查找 Package 器类型来更改方法类型的返回类型/参数类型。
对于getter:

  1. MethodType type = target.type();
  2. if (fieldType.isPrimitive()) {
  3. type = type.changeReturnType(map.get(fieldType));
  4. }

对于setter:

  1. MethodType type = target.type();
  2. if (fieldType.isPrimitive()) {
  3. type = type.changeParameterType(1, map.get(fieldType));
  4. }

如果不清楚,调用方将传递原语getter和setter的原语类:

  1. createSetterViaMethodname(Main.class, "setFoo", int.class)
  2. // for a setter declared like this:
  3. public void setFoo(int i) {
  4. ...
  5. }

完整代码:

  1. public class Main {
  2. public static void main(String[] args) throws Throwable {
  3. // this prints 1234567
  4. createSetterViaMethodname(Main.class, "setFoo", int.class).set(new Main(), 1234567);
  5. }
  6. public void setFoo(int i) {
  7. System.out.println(i);
  8. }
  9. public final static Map<Class<?>, Class<?>> map = new HashMap<>();
  10. static {
  11. map.put(boolean.class, Boolean.class);
  12. map.put(byte.class, Byte.class);
  13. map.put(short.class, Short.class);
  14. map.put(char.class, Character.class);
  15. map.put(int.class, Integer.class);
  16. map.put(long.class, Long.class);
  17. map.put(float.class, Float.class);
  18. map.put(double.class, Double.class);
  19. }
  20. public static <T, R> IGetter<T, R> createGetterViaMethodname( final Class<T> clazz, final String methodName,
  21. final Class<R> fieldType )
  22. throws Throwable
  23. {
  24. final MethodHandles.Lookup caller = MethodHandles.lookup();
  25. final MethodType methodType = MethodType.methodType( fieldType );
  26. final MethodHandle target = caller.findVirtual( clazz, methodName, methodType );
  27. MethodType type = target.type();
  28. if (fieldType.isPrimitive()) {
  29. type = type.changeReturnType(map.get(fieldType));
  30. }
  31. final CallSite site = LambdaMetafactory.metafactory(
  32. caller,
  33. "get",
  34. MethodType.methodType( IGetter.class ),
  35. type.erase(),
  36. target,
  37. type);
  38. final MethodHandle factory = site.getTarget();
  39. return (IGetter<T, R>) factory.invoke();
  40. }
  41. public static <T, I> ISetter<T, I> createSetterViaMethodname( final Class<T> clazz, final String methodName,
  42. final Class<I> fieldType )
  43. throws Throwable
  44. {
  45. final MethodHandles.Lookup caller = MethodHandles.lookup();
  46. final MethodType methodType = MethodType.methodType( void.class, fieldType );
  47. final MethodHandle target = caller.findVirtual( clazz, methodName, methodType );
  48. MethodType type = target.type();
  49. if (fieldType.isPrimitive()) {
  50. type = type.changeParameterType(1, map.get(fieldType));
  51. }
  52. final CallSite site = LambdaMetafactory.metafactory(
  53. caller,
  54. "set",
  55. MethodType.methodType( ISetter.class ),
  56. type.erase(),
  57. target,
  58. type );
  59. final MethodHandle factory = site.getTarget();
  60. return (ISetter<T, I>) factory.invoke();
  61. }
  62. }
  63. @FunctionalInterface
  64. interface IGetter<T, R>
  65. {
  66. @Nullable
  67. R get( T object );
  68. }
  69. @FunctionalInterface
  70. interface ISetter<T, I>
  71. {
  72. void set( T object, @Nullable I value );
  73. }
展开查看全部
dauxcl2d

dauxcl2d2#

另一种方法是使用外部库而不是lambdametafactory。cojen/maker库提供对代码生成的直接控制,并自动执行装箱/拆箱转换。
即使字段类型为int,此示例也可以正常工作。调用方必须直接提供lookup类才能访问任何非公共方法,但在原始示例中这也是必需的。这意味着 <T> 未使用param,但此示例应该足够了。

  1. import org.cojen.maker.ClassMaker;
  2. import org.cojen.maker.MethodMaker;
  3. public class SetterGetterMaker {
  4. public static <T, R> IGetter<T, R> createGetterViaMethodname(MethodHandles.Lookup lookup,
  5. String methodName)
  6. throws Throwable
  7. {
  8. ClassMaker cm = ClassMaker.begin(null, lookup).implement(IGetter.class);
  9. cm.addConstructor();
  10. MethodMaker mm = cm.addMethod(Object.class, "get", Object.class).public_();
  11. mm.return_(mm.param(0).cast(lookup.lookupClass()).invoke(methodName));
  12. var newLookup = cm.finishHidden();
  13. var newClass = newLookup.lookupClass();
  14. var ctorHandle = newLookup.findConstructor(newClass, MethodType.methodType(void.class));
  15. return (IGetter<T, R>) ctorHandle.invoke();
  16. }
  17. public static <T, I> ISetter<T, I> createSetterViaMethodname(MethodHandles.Lookup lookup,
  18. String methodName,
  19. Class<I> fieldType )
  20. throws Throwable
  21. {
  22. ClassMaker cm = ClassMaker.begin(null, lookup).implement(ISetter.class);
  23. cm.addConstructor();
  24. MethodMaker mm = cm.addMethod(void.class, "set", Object.class, Object.class).public_();
  25. var fieldVar = mm.param(1).cast(fieldType);
  26. mm.param(0).cast(lookup.lookupClass()).invoke(void.class, methodName, null, fieldVar);
  27. var newLookup = cm.finishHidden();
  28. var newClass = newLookup.lookupClass();
  29. var ctorHandle = newLookup.findConstructor(newClass, MethodType.methodType(void.class));
  30. return (ISetter<T, I>) ctorHandle.invoke();
  31. }
  32. }
展开查看全部

相关问题