像单个实例一样处理实例数组

时间:2012-12-06 12:19:15

标签: java design-patterns syntactic-sugar

在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?

此外,如果没有,是否有任何语言本身可以促进这一点?

我很感激它并不适用于所有场景,但我认为这取决于程序员的判断。

非常感谢

5 个答案:

答案 0 :(得分:1)

你可以用反射做到这一点。

创建一个泛型类,它在运行时查看T的所有公共方法,并使用相同的签名注入自身方法,并且循环遍历其所有对象并在其上调用相同的方法。

这个问题是如何调用动态创建的方法。由于这只能通过反射实现,这意味着原始代码中的任何方法调用都必须通过反射来完成。

答案 1 :(得分:1)

您正在呼唤Composite Pattern。您可以在项目PerfectJPattern中找到组件化的通用和可重用实现,请务必查看与GoF图书中的示例匹配的Composite documentation pageexample

示例相关部分的逐字副本,假设您有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方法是一种可以用作实用工具方法的方法,并且AB已经被编写了。 所以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);