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