java - LambdaMetaFactory 装箱/拆箱参数和返回类型
问题描述
我有以下两种方法:
public static <T, R> IGetter<T, R> createGetterViaMethodname( final Class<T> clazz, final String methodName,
final Class<R> fieldType )
throws Throwable
{
final MethodHandles.Lookup caller = MethodHandles.lookup();
final MethodType methodType = MethodType.methodType( fieldType );
final MethodHandle target = caller.findVirtual( clazz, methodName, methodType );
final MethodType type = target.type();
final CallSite site = LambdaMetafactory.metafactory(
caller,
"get",
MethodType.methodType( IGetter.class ),
type.erase(),
target,
type );
final MethodHandle factory = site.getTarget();
return (IGetter<T, R>) factory.invoke();
}
public static <T, I> ISetter<T, I> createSetterViaMethodname( final Class<T> clazz, final String methodName,
final Class<I> fieldType )
throws Throwable
{
final MethodHandles.Lookup caller = MethodHandles.lookup();
final MethodType methodType = MethodType.methodType( void.class, fieldType );
final MethodHandle target = caller.findVirtual( clazz, methodName, methodType );
final MethodType type = target.type();
final CallSite site = LambdaMetafactory.metafactory(
caller,
"set",
MethodType.methodType( ISetter.class ),
type.erase(),
target,
type );
final MethodHandle factory = site.getTarget();
return (ISetter<T, I>) factory.invoke();
}
包括以下两个接口:
@FunctionalInterface
public interface IGetter<T, R>
{
@Nullable
R get( T object );
}
@FunctionalInterface
public interface ISetter<T, I>
{
void set( T object, @Nullable I value );
}
这适用于所有类类型,包括用于原始类型的数字包装器,例如Integer
for int
。但是,如果我有一个接受 an 的 setterint
或一个返回 an 的 getter ìnt
,它会尝试传递/返回 Number-Wrapper,从而导致异常。
无需使用其他方法即可将其装箱/拆箱的正确方法是什么。这里的原因是保持 API 干净且易于使用。我愿意为这里的拳击/拆箱带来小小的性能损失。
解决方案
没有内置的“漂亮”方法可以将原始类转换为包装类,因此您必须使用这样的映射:
private final static Map<Class<?>, Class<?>> map = new HashMap<>();
static {
map.put(boolean.class, Boolean.class);
map.put(byte.class, Byte.class);
map.put(short.class, Short.class);
map.put(char.class, Character.class);
map.put(int.class, Integer.class);
map.put(long.class, Long.class);
map.put(float.class, Float.class);
map.put(double.class, Double.class);
}
或者在这里使用其他方法之一。
一旦你这样做了,你就可以检查是否fieldType
是一个原语。如果是,则通过在映射中查找包装器类型来更改方法类型的返回类型/参数类型。
对于吸气剂:
MethodType type = target.type();
if (fieldType.isPrimitive()) {
type = type.changeReturnType(map.get(fieldType));
}
对于二传手:
MethodType type = target.type();
if (fieldType.isPrimitive()) {
type = type.changeParameterType(1, map.get(fieldType));
}
以防万一,调用者将传递原始类以获取原始 getter 和 setter:
createSetterViaMethodname(Main.class, "setFoo", int.class)
// for a setter declared like this:
public void setFoo(int i) {
...
}
完整代码:
public class Main {
public static void main(String[] args) throws Throwable {
// this prints 1234567
createSetterViaMethodname(Main.class, "setFoo", int.class).set(new Main(), 1234567);
}
public void setFoo(int i) {
System.out.println(i);
}
public final static Map<Class<?>, Class<?>> map = new HashMap<>();
static {
map.put(boolean.class, Boolean.class);
map.put(byte.class, Byte.class);
map.put(short.class, Short.class);
map.put(char.class, Character.class);
map.put(int.class, Integer.class);
map.put(long.class, Long.class);
map.put(float.class, Float.class);
map.put(double.class, Double.class);
}
public static <T, R> IGetter<T, R> createGetterViaMethodname( final Class<T> clazz, final String methodName,
final Class<R> fieldType )
throws Throwable
{
final MethodHandles.Lookup caller = MethodHandles.lookup();
final MethodType methodType = MethodType.methodType( fieldType );
final MethodHandle target = caller.findVirtual( clazz, methodName, methodType );
MethodType type = target.type();
if (fieldType.isPrimitive()) {
type = type.changeReturnType(map.get(fieldType));
}
final CallSite site = LambdaMetafactory.metafactory(
caller,
"get",
MethodType.methodType( IGetter.class ),
type.erase(),
target,
type);
final MethodHandle factory = site.getTarget();
return (IGetter<T, R>) factory.invoke();
}
public static <T, I> ISetter<T, I> createSetterViaMethodname( final Class<T> clazz, final String methodName,
final Class<I> fieldType )
throws Throwable
{
final MethodHandles.Lookup caller = MethodHandles.lookup();
final MethodType methodType = MethodType.methodType( void.class, fieldType );
final MethodHandle target = caller.findVirtual( clazz, methodName, methodType );
MethodType type = target.type();
if (fieldType.isPrimitive()) {
type = type.changeParameterType(1, map.get(fieldType));
}
final CallSite site = LambdaMetafactory.metafactory(
caller,
"set",
MethodType.methodType( ISetter.class ),
type.erase(),
target,
type );
final MethodHandle factory = site.getTarget();
return (ISetter<T, I>) factory.invoke();
}
}
@FunctionalInterface
interface IGetter<T, R>
{
@Nullable
R get( T object );
}
@FunctionalInterface
interface ISetter<T, I>
{
void set( T object, @Nullable I value );
}
推荐阅读
- python - 按年份对出版物列表进行排序
- node.js - Heroku 上的 Node.js Web 应用程序的 Auth0 登录失败(但在 localhost 上成功)
- angular - Angular Material Sidenav 模块无法识别
- stored-procedures - 你能解释一下这个 db2 SQL 错误:“处理字符串附近的条件编译指令时发生错误。原因代码=r2
- javascript - 尝试在 SQL DB 中存储文件路径名时,为什么在 Postman 中返回 null?
- html - 如何在没有页脚标签的引导模式对话框中减少底部的空白空间
- json - Django视图处理太慢
- php - 如何在 Web 服务器中复制同一 php 应用程序的多个实例的部署
- javascript - 在没有输入元素的情况下使用 keypress 或 keydown 事件
- python - 当我运行以下代码时,仅显示偶数行。有人可以解释为什么会这样