在java中使用Method.invoke时忽略异常检查

在java中使用Method.invoke时忽略异常检查,java,reflection,exception-handling,Java,Reflection,Exception Handling,在我参与的一个项目中,有大量的类,每个类都实现了一个名为add的方法,它们都以相同的方式工作,例如MyVector sum=add(vector1,vector2),其中vector1和vector2都是MyVector类型。我没有权限修改所有具有add的类,因此我可以让它们实现一些接口“IAddable” 现在,我想创建一个表单的泛型类 class Summinator<TVector> { Function<TVector,TVector,TVector> a

在我参与的一个项目中,有大量的类,每个类都实现了一个名为
add
的方法,它们都以相同的方式工作,例如
MyVector sum=add(vector1,vector2)
,其中
vector1
vector2
都是
MyVector
类型。我没有权限修改所有具有
add
的类,因此我可以让它们实现一些接口“
IAddable

现在,我想创建一个表单的泛型类

class Summinator<TVector>
{
    Function<TVector,TVector,TVector> add;

    public Summinator()
    {
        //... somehow get the class of TVector, say cVector
        Method addMethod = cVector.getDeclaredMethod("add", new Class[] {cVector, cVector});
        add = (v1, v2) -> (TVector)addMethod.invoke(null, v1, v2);
    }

    public TVector VeryLargeSum(TVector[] hugePileOfVectors)
    {
        TVector sum = hugePileOfVectors[0];
        for (int i = 1; i < hugePileOfVectors.length; i++)
        {
            sum = add(sum, hugePileOfVectors[i]);
        }
        return sum;
    }
}
它迫使我写一些类似的东西

add = (v1, v2) -> 
{
    try {
    return add.invoke(null, v1, v2);
    } catch (IllegalAccessException e) {
         e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
};
我担心这种异常检查将消耗大量的机器时间,而实际上所有对象的性质都非常基本,而
add
的应用实际上是一个失败的问题。如果是C#,并且所有相关的类都有一个重载+操作,那么我就可以使用支持的二进制操作解决System.Linq.Expressions包的问题:没有强制的异常检查。但是我将在java工作


也许,至少有一种方法可以忽略异常检查?

如果您想要一些实用程序,可以简化数组中不同对象的所有实例的求和,那么您应该使用Java 8中的函数引用和流

假设您有一个类
MyVector
,带有静态
add
函数:

/**
 * Class with static add method.
 */
public class MyVector {

    public static MyVector add(MyVector one, MyVector two) {
        return new MyVector();
    }

}
然后,可以通过以下方式进行数组求和:

import java.util.stream.Stream;

public class Summing {

    public static void main(String args[]) {
        MyVector[] myValues = new MyVector[]{/* values */};
        MyVector sum = Stream.of(myValues).reduce(MyVector::add).get();
    }

}
如果
add
是一种实例方法,那么情况会变得有点棘手。但假设它不依赖于对象属性:

/**
 * Class with instance add method.
 */
public class OtherVector {

    public OtherVector add(OtherVector one, OtherVector two) {
        return new OtherVector();
    }

}
您可以应用以下内容:

import java.util.function.BinaryOperator;
import java.util.stream.Stream;

public class Summing {

    public static void main(String args[]) {
        OtherVector[] otherValues = new OtherVector[]{/* values */};
        // If add is an instance method than you need to decide what instance you want to use
        BinaryOperator<OtherVector> otherAdd = new OtherVector()::add;
        OtherVector sum = Stream.of(otherValues).reduce(otherAdd).get();
    }

}
import java.util.function.BinaryOperator;
导入java.util.stream.stream;
公共类汇总{
公共静态void main(字符串参数[]){
OtherVector[]otherValues=新的OtherVector[]{/*值*/};
//如果add是一个实例方法,那么您需要决定要使用哪个实例
BinaryOperator otherAdd=新的OtherVector()::add;
OtherVector sum=Stream.of(otherValues).reduce(otherAdd.get();
}
}

在你们班上,少了一些东西。您正在范围内的
cVector
上调用
getDeclaredMethod
。由于类型擦除,泛型类不可能单独获得参数化的
对象,因此为了使该类正常工作,必须有人使用实际的类型参数化实例化
求和器
,并传递适当的
对象,例如

Summinator<Actual> actualSumminatior = new Summinator<>(Actual.class);
并将呼叫者更改为

在使用站点,使整个
Summinator
过时


如果调用代码是一个不可更改的遗留代码库,并且您必须使用
输入,那么您可以动态生成等效于方法引用的代码:

class Summinator<TVector>
{
    final BinaryOperator<TVector> add;

    public Summinator(Class<TVector> cVector)
    {
        MethodHandles.Lookup l = MethodHandles.lookup();
        MethodType addSignature = MethodType.methodType(cVector, cVector, cVector);
        try
        {
            MethodHandle addMethod = l.findStatic(cVector, "add", addSignature);
            add = (BinaryOperator<TVector>)LambdaMetafactory.metafactory(l, "apply",
                  MethodType.methodType(BinaryOperator.class),
                  addSignature.erase(), addMethod, addSignature)
                .getTarget().invokeExact();
        }
        catch(RuntimeException|Error t)
        {
            throw t;
        }
        catch(Throwable t) {
            throw new IllegalArgumentException("not an appropriate type "+cVector, t);
        }
    }

    public TVector VeryLargeSum(TVector[] hugePileOfVectors)
    { // if hugePileOfVectors is truly huge, this can be changed to parallel execution
        return Arrays.stream(hugePileOfVectors).reduce(add)
            .orElseThrow(() -> new IllegalArgumentException("empty array"));
    }
}
类求和器
{
最终二进制运算符add;
公共汇总器(C类汇总器)
{
MethodHandles.Lookup l=MethodHandles.Lookup();
MethodType addSignature=MethodType.MethodType(cVector,cVector,cVector);
尝试
{
MethodHandle addMethod=l.findStatic(cVector,“add”,addSignature);
add=(BinaryOperator)LambdaMetafactory.metafactory(l,“应用”,
MethodType.MethodType(BinaryOperator.class),
addSignature.erase(),addMethod,addSignature)
.getTarget().invokeExact();
}
捕获(运行时异常|错误t)
{
掷t;
}
捕获(可丢弃的t){
抛出新的IllegalArgumentException(“不是合适的类型”+cVector,t);
}
}
公共TVector VeryLargeSum(TVector[]hugePileOfVectors)
{//如果hugePileOfVectors确实很大,那么可以将其更改为并行执行
返回Arrays.stream(hugePileOfVectors.reduce(add)
.orelsetrow(()->新的IllegalArgumentException(“空数组”);
}
}

请注意,这两种解决方案的运行速度可能都比
方法快。基于invoke
,但这并不是因为函数中没有异常处理,而是因为通过反射进行调用通常比较昂贵。

如果没有引发异常,则异常检查在运行时不会花费任何时间。所以你问题的前提是不正确的。还要注意,所有Java库方法名称都遵循Java编码约定,因此它是
method.invoke
(小写)而不是
method.invoke
。应尽可能避免反射。你们有几门不同的课?您可能可以创建一个
Map对不起,我现在使用的是
Map
,因为我的方法也是内置类型,如
int
double
。困扰我的是,这会使项目更难扩展,除了我必须在类似类可以访问的其他类中实现map,而不是在“
Summatator
”中实现map。无论如何,这意味着,每当出现一种新的“向量”时,就要修改映射。问题的根本原因显然是您没有一个具有重写的
add
方法的共享基类,或者它们都实现的接口。这是非常非面向对象的,如果你不能解决这个问题,你将不得不用丑陋的黑客来解决它。“无论如何,这意味着,每当一种新的“向量”出现时,地图都会被修改。”你说-是的,这是这个糟糕的设计(你可能继承了)的直接结果,主要思想是要有一个通用的“
summator
”类,因为我可能会在运行时实例化其中的几个,每一种都适用于不同类型的向量。假设所有向量都有一个静态
add
方法。然后是一个愚蠢的问题:下面的代码会工作吗?(当然我可以自己试试)
I
class Summinator<TVector>
{
    final BinaryOperator<TVector> add;

    public Summinator(BinaryOperator<TVector> addFunction)
    {
        add = addFunction;
    }

    public TVector VeryLargeSum(TVector[] hugePileOfVectors)
    {
        TVector sum = hugePileOfVectors[0];
        for (int i = 1; i < hugePileOfVectors.length; i++)
        {
            sum = add.apply(sum, hugePileOfVectors[i]);
        }
        return sum;
    }
}
Summinator<Actual> actualSumminatior = new Summinator<>(Actual::add);
return Arrays.stream(hugePileOfVectors).reduce(Actual::add)
    .orElseThrow(() -> new IllegalArgumentException("empty array"));
class Summinator<TVector>
{
    final BinaryOperator<TVector> add;

    public Summinator(Class<TVector> cVector)
    {
        MethodHandles.Lookup l = MethodHandles.lookup();
        MethodType addSignature = MethodType.methodType(cVector, cVector, cVector);
        try
        {
            MethodHandle addMethod = l.findStatic(cVector, "add", addSignature);
            add = (BinaryOperator<TVector>)LambdaMetafactory.metafactory(l, "apply",
                  MethodType.methodType(BinaryOperator.class),
                  addSignature.erase(), addMethod, addSignature)
                .getTarget().invokeExact();
        }
        catch(RuntimeException|Error t)
        {
            throw t;
        }
        catch(Throwable t) {
            throw new IllegalArgumentException("not an appropriate type "+cVector, t);
        }
    }

    public TVector VeryLargeSum(TVector[] hugePileOfVectors)
    { // if hugePileOfVectors is truly huge, this can be changed to parallel execution
        return Arrays.stream(hugePileOfVectors).reduce(add)
            .orElseThrow(() -> new IllegalArgumentException("empty array"));
    }
}