Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/328.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么Java不支持在调用方法时访问原语返回值而不通过反射装箱?_Java_Methods_Reflection_Return Value_Primitive Types - Fatal编程技术网

为什么Java不支持在调用方法时访问原语返回值而不通过反射装箱?

为什么Java不支持在调用方法时访问原语返回值而不通过反射装箱?,java,methods,reflection,return-value,primitive-types,Java,Methods,Reflection,Return Value,Primitive Types,我注意到Java反射支持对基本类型的字段(如boolean或int)的访问,而无需装箱: public final class Field extends AccessibleObject implements Member { //... public boolean getBoolean(Object obj) throws IllegalArgumentException, IllegalAccessException { ... } public char get

我注意到Java反射支持对基本类型的字段(如boolean或int)的访问,而无需装箱:

public final class Field extends AccessibleObject implements Member {
    //...
    public boolean getBoolean(Object obj) throws IllegalArgumentException, IllegalAccessException { ... }
    public char getChar(Object obj) throws IllegalArgumentException, IllegalAccessException { ... }
    public byte getByte(Object obj) throws IllegalArgumentException, IllegalAccessException { ... }
    public short getShort(Object obj) throws IllegalArgumentException, IllegalAccessException { ... }
    public int getInt(Object obj) throws IllegalArgumentException, IllegalAccessException { ... }
    public long getLong(Object obj) throws IllegalArgumentException, IllegalAccessException { ... }
    public float getFloat(Object obj) throws IllegalArgumentException, IllegalAccessException { ... }
    public double getDouble(Object obj) throws IllegalArgumentException, IllegalAccessException { ... }
    //...
}
然而,对于总是返回对象的方法,只有invoke(…)。当与具有原语返回值的方法一起使用时,这将强制装箱。 我想知道为什么还没有人支持这一点。是否有人要求这样做,或者是否存在严重问题阻止这样做

有没有严重的问题阻止它

是的,返回值不是装箱原语的
invoke()
的唯一部分,方法参数也是装箱的

例如,如果您有方法
boolean foo(字符串a、int b、double c)
,那么您可以这样调用它:

字符串a=。。。;
int b=。。。;
双c=。。。;
布尔结果=方法调用(obj,a,b,c);
在自动装箱和varargs之前,应该是这样的,这是编译器实际生成的:

boolean result=method.invoke(obj,
新对象[]{a,
整数值(b)/*自动装箱*/,,
Double.valueOf(c)/*自动装箱*/})
.booleanValue()/*自动取消装箱*/;
为了消除对原语装箱的需要,API需要提供一个重载的
invoke()
方法,该方法与方法的签名完全匹配,而不仅仅是与返回类型匹配的方法

必须有数以百万计的过载,这将是一个严重的问题

对各种返回类型进行重载而不同时对参数进行重载是没有意义的,因为您想用它来修复什么?没什么


进行自反方法调用的开销很高,因此返回值的装箱是不成问题的。

有一种方法,但您需要“更新的”反射。例如:

static class Test {

    public long go(int x, int y) {
        return x + y;
    }

}


static void methodHandles() throws Throwable {
    MethodType mt = MethodType.methodType(long.class, int.class, int.class);
    MethodHandles.Lookup lookup = MethodHandles.lookup();
    MethodHandle goMethod = lookup.findVirtual(Test.class, "go", mt);

    long result = (long)goMethod.invokeExact(new Test(), 40, 2);
    System.out.println(result);
}
这将编译为:

Method java/lang/invoke/MethodHandle.invokeExact:(LDeleteMe$Test;II)J

注意签名:
…II)L
,而不是
java/lang/Integer
java/lang/Long
。这些也被称为编译器重载。

很难想象会有任何问题阻止它-一些简单的包装器方法,如
public int invokeInt(…){return((Integer)invoke(…).intValue();}
可以完成这项工作。我怀疑这是因为通过反射访问私有字段或对象的所有字段更为常见,并且很少需要动态调用私有方法或对象上的所有方法。@kaya3或者您可以使用
MethodHandle
s来生成适当的字节码,我猜。@Eugene感谢您提供了带有方法句柄的代码示例。我很快会亲自尝试一下,@Andreas的回答完全正确,但我要补充一点:我认为这个问题被“杯子1%空”的错误观点所困扰。(一个更好的问题可能是:“Field为什么还要为取消装箱访问而烦恼?”)此外,您真的确定装箱实际上会对热路径产生性能影响,还是您只是在猜测?我预计这对实际成本的贡献很小。(还有,是否需要说,如果您在性能关键的热门代码路径上有反射调用,也许这就是您的问题所在?@Brian Goetz坦率地说:我猜。如果装箱过于频繁,我担心我的框架解决方案以后无法用于大数据。不过,拳击的特殊处理方法并不是你以后可以介绍的。然而,我同意我必须用JMH来衡量性能,并找出慢的部分。是的,反思性的呼叫可能非常缓慢,拳击可能不是主要问题。感谢您的回答、评论和链接。他们给了我很多帮助,给了我很好的建议。我想到了另外7种方法,比如invokeBoolean(Object,Object…args)。那就够了!当然,不能避免使用args装箱,但可以使用返回值装箱。如果您想到的getter从来没有参数,但通常返回原语值,那么这仍然非常有用。特别是如果POJO或实体被代理,因此您只能看到getter(例如,当您仅定义getter和setter接口时)。@mmirwaldt但仅执行返回类型是一个不完整的解决方案,并且装箱开销与自反调用本身相比非常小,那么您试图解决什么问题?您是否需要编写
boolean x=(boolean)invoke()
boolean x=invokeBoolean()
与您必须编写的内容或对其功能的理解几乎没有区别。我看不出有什么理由不做点什么就尝试这个。用重载方法填充
Method
类以获得如此小的收益是过分的。那么,为什么字段中存在这些getter方法呢?此外,还有7种方法不会使方法爆炸;-)我是说,如果你只有一些(@mmirwaldt这7是从哪里来的呢?
字段
类除了handle之外还有16个方法接受或返回
对象
。为什么
方法
的返回类型比参数类型更适合重载?为什么要考虑通过Ref读取100000字段的坐标选择?如果您关心性能,就不要使用反射。毫无疑问,太多的装箱是不可取的。这就是为什么存在,例如,
IntStream
,而不是仅仅依赖
Stream
。这就是为什么存在反射的替代方案,
MethodHandle
,并且如果您确实必须调用特定的met通常,首先考虑通过<代码> LambdaMetaFactory < /代码>将<代码>方法句柄<代码>转换为具有匹配签名的接口实现。而您所概述的技术确实允许无框调用,称之为“新反射”。