在 java 中使用 Method.invoke 时忽略异常检查
Ignoring exception checking while using Method.invoke in java
在我参与的一个项目中,有很多 classes,每个都实现了一个名为 add
的方法,它们都以相同的方式工作,例如MyVector sum = add(vector1, vector2)
,其中 vector1
和 vector2
都是 MyVector
类型。我无权修改所有具有 add
的 classes,所以我可以让它们实现一些接口“IAddable
”。
现在,我想制作一个通用的 class 形式
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# 并且所有有问题的 classes 都有一个重载 + 操作,我可以用 System.Linq.Expressions 包解决我的问题,使用支持的二进制操作:异常检查不是强制性的。但是...我要在 java.
工作
也许,有一些方法可以至少忽略异常检查?
如果您想要一些实用程序来简化数组中不同对象的所有实例的总和,您应该使用 Java 8.
中的函数引用和流
假设您有一些 class 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();
}
}
在你的 class 中,少了一些东西。您在 cVector
上调用 getDeclaredMethod
,而该 cVector
不在范围内。由于类型擦除,泛型 class 不可能自己获得参数化的 Class
对象,因此要使此 class 正常工作,必须有人实例化 Summinator
使用实际类型参数化 和 传递适当的 Class
对象,例如
Summinator<Actual> actualSumminatior = new Summinator<>(Actual.class);
最干净的解决方案是更改 Summinator
class 让此实例化代码首先通过预期的 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
的解决方案更快,但这并不是因为函数中没有异常处理,而是因为通过反射调用通常很昂贵。
在我参与的一个项目中,有很多 classes,每个都实现了一个名为 add
的方法,它们都以相同的方式工作,例如MyVector sum = add(vector1, vector2)
,其中 vector1
和 vector2
都是 MyVector
类型。我无权修改所有具有 add
的 classes,所以我可以让它们实现一些接口“IAddable
”。
现在,我想制作一个通用的 class 形式
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# 并且所有有问题的 classes 都有一个重载 + 操作,我可以用 System.Linq.Expressions 包解决我的问题,使用支持的二进制操作:异常检查不是强制性的。但是...我要在 java.
也许,有一些方法可以至少忽略异常检查?
如果您想要一些实用程序来简化数组中不同对象的所有实例的总和,您应该使用 Java 8.
中的函数引用和流假设您有一些 class 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();
}
}
在你的 class 中,少了一些东西。您在 cVector
上调用 getDeclaredMethod
,而该 cVector
不在范围内。由于类型擦除,泛型 class 不可能自己获得参数化的 Class
对象,因此要使此 class 正常工作,必须有人实例化 Summinator
使用实际类型参数化 和 传递适当的 Class
对象,例如
Summinator<Actual> actualSumminatior = new Summinator<>(Actual.class);
最干净的解决方案是更改 Summinator
class 让此实例化代码首先通过预期的 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
的解决方案更快,但这并不是因为函数中没有异常处理,而是因为通过反射调用通常很昂贵。