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

时间:2017-11-22 14:24:54

标签: java reflection exception-handling

在我参与的一个项目中,有很多类都实现了一个名为add的方法,它们的工作方式相同,例如MyVector sum = add(vector1, vector2),其中vector1vector2都属于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;
    }
}

由于总和很大,我希望有一个lambda表达式来完成这项工作。我也在启动时进行类型检查。但是,每次调用方法时,java都要我检查异常,而不是

add = (v1, v2) -> (TVector)addMethod.Invoke(null, v1, v2);

它迫使我写出像

这样的东西
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工作。

也许,至少可以忽略异常检查?

2 个答案:

答案 0 :(得分:2)

在你的课堂上,缺少一些东西。您在范围无处的getDeclaredMethod上呼叫cVector。由于类型擦除,泛型类不可能自己获取参数化的Class对象,因此要使此类正常工作,必须有人使用实际类型实例化Summinator参数化传递适当的Class对象,例如

Summinator<Actual> actualSumminatior = new Summinator<>(Actual.class);

最干净的解决方案是更改Summinator类,让这个实例化代码首先通过预期的add函数:

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);

就是这样。

但请注意,VeryLargeSum的整个操作可以简化为

return Arrays.stream(hugePileOfVectors).reduce(Actual::add)
    .orElseThrow(() -> new IllegalArgumentException("empty array"));

在使用网站上,使整个Summinator过时。

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

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"));
    }
}

请注意,两个解决方案可能比基于Method.invoke的解决方案运行得更快,但不是因为函数中没有异常处理,而是因为通过Reflection调用通常很昂贵。

答案 1 :(得分:0)

如果你想要一些实用程序,可以很容易地将数组中不同对象的所有实例相加,你应该在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();
    }

}