在Java中,我正在寻找一个通用模板,这意味着给定类型的数组(比如Foo
)将允许对数组进行实例方法调用。在幕后,这将转化为迭代数组中的所有Foo实例,并在每个实例上调用实例方法。
也许有些代码会更好地证明这一点:
public class Foo{
public Foo(){}
public void Method1(){}
}
所以你有:
Foo foo = new Foo();
foo.Method1();
但是,您是否可以为自定义类型制作一些通用模板,这些模板固有地使这种事情成为可能:
Foo[] foos = new Foo[]{new Foo(),new Foo(), new Foo()};
foos.Method1();
这主要是语法糖:
foreach(Foo f : foos){
f.Method1();
}
我的动机是,有人可以使用varargs:
someHelper(fooInstance1,fooInstance2).Method1()
someHelper()
返回Foo[]
。
如果每个Method1()
调用返回一个值,那么如果它被包含在返回值数组中(其中ReturnVals.size == Foos.size
)会更好。
在最坏的情况下,我必须编写一个单独的类来为我需要的每种类型实现此功能,可能使用接口来描述适用于单个实例和实例数组的功能。
是否可以优雅地实现Java魔术,设计模式或通用jiggery-pokery?
此外,如果没有,是否有任何语言本身可以促进这一点?
我很感激它并不适用于所有场景,但我认为这取决于程序员的判断。
非常感谢
答案 0 :(得分:1)
你可以用反射做到这一点。
创建一个泛型类,它在运行时查看T的所有公共方法,并使用相同的签名注入自身方法,并且循环遍历其所有对象并在其上调用相同的方法。
这个问题是如何调用动态创建的方法。由于这只能通过反射实现,这意味着原始代码中的任何方法调用都必须通过反射来完成。
答案 1 :(得分:1)
您正在呼唤Composite Pattern。您可以在项目PerfectJPattern中找到组件化的通用和可重用实现,请务必查看与GoF图书中的示例匹配的Composite documentation page和example。
示例相关部分的逐字副本,假设您有IGraphic界面和一些实现,例如矩形和直线,然后就可以了:
// build the composite
IComposite<IGraphic> myComposite = new Composite<IGraphic>(IGraphic.class);
myComposite.add(new Rectangle());
myComposite.add(new Line());
myComposite.add(new Line());
// use the composite, invokes the IGraphic#draw() in the
// underlying Rectangle and two Line instances
myComposite.getComponent().draw();
这是针对您的具体案例的方式:
Foo fooInstance1 = new Foo();
Foo fooInstance2 = new Foo();
IComposite<Foo> myComposite = new Composite<Foo>(Foo.class);
myComposite.add(fooInstance1);
myComposite.add(fooInstance2);
// invokes Method1 on instance1 and instance2 transparently
myComposite.getComponent().Method1();
// alternatively do
Foo myCompositeFoo = myComposite.getComponent();
// pass this myCompositeFoo around and do
myCompositeFoo.Method1();
请注意,IComposite
可重用实现包含实际的复合,不实现/提供Foo
接口,您必须通过{获取它{1}}方法。这是一个小麻烦,它是必需的,因为在Java中没有其他方法可以创建实现任何任意和静态未知接口的某个实例(在本例中为getComponent
)。我能做的最好的事情就是给你一个Composite
,为你构建一个真正的复合组件并返回你想要的接口类型Composite
。这是使用动态代理实现的,但实现是类型安全的并且完全组件化,即您不必创建任何实现您的接口的新复合数组。
答案 2 :(得分:1)
有很多方法可以做到这一点,大多数使用包装器对象等... 但是,如果你想要的类型是一个界面,你很幸运,这实际上是可行的 使用dynamic proxies。
E.g。
public static void main(String[] args) throws IOException {
B[] objs = {new B(), new B(), new B()};
A oneForAll = createMulticastObject(objs, A.class);
oneForAll.print();
}
@SuppressWarnings("unchecked")
public static <T, U extends T> T createMulticastObject(final U[] targets, Class<T> interfaceClass) {
return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[] {interfaceClass}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object ret = null;
for (U target : targets) {
ret = method.invoke(target, args);
}
return ret;
}
});
}
interface A {
void print();
}
static class B implements A {
@Override
public void print() {
System.out.println("Invoked.");
}
}
明显的限制是A
(这是你在多播方法调用中使用的对象类型必须是一个接口。
另一个明显的限制是多播对象只能返回所有这些调用的单个返回。
代码在一个地方可能看起来很丑,但是createMulticastObject
方法是一种可以用作实用工具方法的方法,并且A
和B
已经被编写了。
所以dynamic proxies
将通常的包装等减少到:
A oneForAll = createMulticastObject(objs, A.class);
答案 3 :(得分:0)
查看apache的CollectionUtils中的方法。您必须为要在列表中调用的每个方法实现一个接口。
示例:
CollectionUtils.collect(foos, new Transformer() {
public Object transform(Object foo) {
return ((Foo) foo).method();
}
});
这里我使用了一个任意类,但你也可以在一个单独的类中实现Transformer并重用它。
我不知道是否存在具有泛型的版本。
还可以尝试使用功能语言(scala,haskell,...)。他们使这种事情变得非常容易。
答案 4 :(得分:0)
是否可以优雅地实现Java魔术,设计模式或通用jiggery-pokery?
不是真的 - 不是没有很多样板文件,这意味着自己编写该死的循环的代码更少。
此外,如果没有,是否有任何语言本身可以促进这一点?
大多数功能性语言支持类似的功能。在Scala中,您可以执行foos.foreach(_.Method1())
,其中foreach
是一个在所有元素上调用代码块的方法,而下划线是当前元素的别名。可运行代码:http://ideone.com/QRtPlK
在Java 8中,您将能够使用集合的流视图以及方法引用来执行以下操作:
foos.stream().forEach(Foo::method1);