在我参与的一个项目中,有很多类都实现了一个名为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;
}
}
由于总和很大,我希望有一个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工作。
也许,至少可以忽略异常检查?
答案 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();
}
}