4

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

我的动机是有人可以使用可变参数,这样:

someHelper(fooInstance1,fooInstance2).Method1()

哪里someHelper()返回Foo[].

如果每次调用都返回一个值,那么将其包装到一个返回值数组(其中)Method1()中会更好。ReturnVals.size == Foos.size

在最坏的情况下,我必须为我需要为此工作的每种类型编写一个单独的类来实现这一点,可能使用接口来描述适用于单个实例和实例数组的功能。

是否有任何 Java 魔法、设计模式或通用 jiggery-pokery 可以优雅地实现这一点?

此外,如果没有,是否有任何语言在本质上促进了这一点?

我很欣赏它不适用于所有场景,但我认为这是由程序员自行决定的。

非常感谢

4

5 回答 5

1

你可以通过反射来做到这一点。

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

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

于 2012-12-06T12:29:51.713 回答
1

您正在呼吁复合模式您可以在项目PerfectJPattern中找到组件化的通用和可重用实现,确保查看 Composite文档页面以及与 GoF 书中示例匹配的示例。

示例相关部分的逐字复制,假设您有 IGraphic 接口和一些实现,例如 Rectangle 和 Line,那么您可以这样做:

// 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接口,您宁愿通过getComponent方法来获取它。这是一个小麻烦,它是必需的,因为在 Java 中没有其他方法可以创建Composite实现任何任意和静态未知接口的事物的实例(在本例中)。我能做的最好的就是给你一个Composite在下面为你构建一个真正的复合组件并返回你想要的接口类型Foo。这是使用动态代理实现的,但该实现是类型安全的并且完全组件化,即您不必创建任何新的复合数组来实现您的接口。

于 2012-12-06T12:38:57.053 回答
1

有多种方法可以做到这一点,大多数使用包装对象等......但是,如果您想要的类型是接口,那么您很幸运,这实际上可以使用动态代理来实现。

例如

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);

于 2012-12-06T13:02:43.267 回答
0

是否有任何 Java 魔法、设计模式或通用 jiggery-pokery 可以优雅地实现这一点?

不是真的 - 不是没有很多样板,这意味着你自己编写该死的循环的代码更少。

此外,如果没有,是否有任何语言在本质上促进了这一点?

大多数函数式语言都支持类似的东西。在 Scala 中,您可以这样做foos.foreach(_.Method1()),其中foreach是在所有元素上调用代码块的方法,下划线是当前元素的别名。可运行代码:http: //ideone.com/QRtPlK

在 Java 8 中,您将能够使用集合的流视图以及方法引用来执行以下操作:

foos.stream().forEach(Foo::method1);
于 2012-12-06T13:31:59.587 回答
0

查看 apaches 的CollectionUtils中的方法。您必须为要在列表上调用的每个方法实现一个接口。

例子:

CollectionUtils.collect(foos, new Transformer() {
    public Object transform(Object foo) {
        return ((Foo) foo).method();
    }
});

这里我使用了一个 anymous 类,但你也可以在一个单独的类中实现 Transformer 并重用它。

我不知道是否存在具有泛型的版本。

还可以尝试一种功能语言(scala,haskell,...)。他们使这种事情很容易做到。

于 2012-12-06T12:31:23.860 回答