用Java模拟鸭子打字 [英] Simulating duck typing in Java
问题描述
问题:我想能够通用访问Java中的 Java类的任何属性/字段,类似于动态语言(认为Groovy, JavaScript)会。当我写这个管道代码的时候,我不会知道它是什么类型的对象,或者属性/域名是什么。但我会在使用它时知道属性/字段名称。
我目前的解决方案:到目前为止,我已经写了一个简单的包装类使用 java.beans.Introspector
来获取Bean / POJO的属性并将它们公开为 Map< String,Object>
。这很简单,但适用于简单情况。
我的问题是除了反射/转换为Map之外还有哪些方法可用于解决此问题?
在我走得太远的路上,我想知道是否有人知道我可以怎样从犀牛或可能 javax.script。*
,它具有深思熟虑的这个概念的实现。或者可能是我没有考虑过的完全不同的方法。
编辑:是的,我熟悉反思(我相信是反思无论如何Introspector正在罩下使用)。我只是好奇,是否还有其他经过深思熟虑的解决方案。
编辑2:看起来最受欢迎的答案包括1)反思直接或通过助手类,和/或2)映射到实现所需类成员的接口。谈到利用Groovy的评论,我真的很感兴趣。由于Groovy具有真正的duck-typing,并且它是一种JVM语言,有没有办法在Groovy中创建一个简单的助手并从Java调用它?这真的很酷,可能更灵活,表现更好。
答案:我将Mike的答案标记为最好的,因为它是一个完整的概念这是最接近的。我可能不会为这种特殊情况走这条路,但它肯定是一种有用的方法。任何人都应该仔细阅读这里的对话,因为这里有很多有用的信息。
谢谢!
$ b $如果你知道你想公开的一组API,假如你知道你想访问一个长度方法和一个迭代器方法,你可以定义一个接口: public interface TheInterfaceIWant {
int length();
void quack();
}
,您希望能够使用此接口访问实例上的相应方法那些没有实现这个接口的,你可以使用代理类: http://download.oracle.com/javase/1.4.2/docs/api/java/lang/reflect/Proxy.html
<所以你创建一个代理
final Object aDuck = ...;
TheInterfaceIWant aDuckWrapper =(TheInterfaceIWant)Proxy.newProxyInstance(
TheInterfaceIWant.class.getClassLoader(),
new Class [] {TheInterfaceIWant.class},
new InvocationHandler(){
public Object invoke(
Object proxy,Method method,Object [] args)
throws Throwable {
return aDuck.getClass()。getMethod(
method.getName( ),method.getParameterTypes())。invoke(aDuck,args);
}
});
然后,您可以像使用动态类型语言的鸭子一样使用包装器。
if(aDuckWrapper.length()> 0){
aDuckWrapper.quack();
$ b 下面是一个全长可运行示例,它使用一个包装器打印四次Quack :
import java.lang.reflect。*;
public class Duck {
//我们用来访问duck typed对象的接口。
public interface TheInterfaceIWant {
int length();
void quack();
}
//不实现TheInterfaceIWant的底层实例!
static final class Foo {
public int length(){return 4; }
public void quack(){System.out.println(Quack); }
}
public static void main(String [] args)抛出Exception {
//创建一个实例,但抛弃所有有用的类型信息。
final Object aDuck = new Foo();
TheInterfaceIWant aDuckWrapper =(TheInterfaceIWant)Proxy.newProxyInstance(
TheInterfaceIWant.class.getClassLoader(),
new Class [] {TheInterfaceIWant.class},
new InvocationHandler (){
public Object invoke(
Object proxy,Method method,Object [] args)
throws Throwable {
return aDuck.getClass()。getMethod(
method.getName(),method.getParameterTypes())。invoke(aDuck,args);
}
});
for(int n = aDuckWrapper.length(); --n> = 0;){
//在这里调用一个Duck.quack()将是无效的,因为它是一个Object。
DuckWrapper.quack();
}
}
}
The problem: I'd like to be able to generically access in Java any property/field on a Java ojbect similarly to how a dynamic language (think Groovy, JavaScript) would. I won't know at the time I'm writing this plumbing code what type of object it is or what the property/field name will be. But I will know the property/field name when I go to use it.
My current solution: So far I've written a simple wrapper class that uses java.beans.Introspector
to grab the properties of a Bean/POJO and expose them as a Map<String, Object>
. It's crude but works for simple cases.
My question is what other methodologies are there for approaching this problem besides reflection / converting to a Map?
Before I go too much further down this path, I'd like to know if anyone knows how I could cannibalize something out of Rhino or perhaps javax.script.*
which has a well thought out implementation of this concept. Or perhaps an entirely different approach that I haven't considered.
Edit: yes I'm familiar with reflection (which I believe is what Introspector is using under the hood anyway). I was just curious if there was any other well thought out solutions.
Edit 2: It appears that the most popular answers involve 1) reflection either directly or via helper classes, and/or 2) mapping to interfaces which implement the desired class members. I'm really intrigued by the comment which talks about leveraging Groovy. Since Groovy has true duck-typing and it is a JVM language, is there a way to make a simple helper in Groovy and call it from Java? This would be really cool and probably more flexible and perform better.
Answer: I marked Mike's answer as the best since it is a complete concept which comes the closest. I probably won't go that route for this particular case, but it is certainly a useful approach. Anyone looking through this should be sure to read the conversations on here as there is a lot of useful info in there as well.
Thanks!
解决方案 If you know the set of APIs that you want to expose, say you know you want access to a length method and an iterator method, you can define an interface:
public interface TheInterfaceIWant {
int length();
void quack();
}
and you want to be able to use this interface to access corresponding methods on instances that do not implement this interface, you can use Proxy classes : http://download.oracle.com/javase/1.4.2/docs/api/java/lang/reflect/Proxy.html
So you create a proxy
final Object aDuck = ...;
TheInterfaceIWant aDuckWrapper = (TheInterfaceIWant) Proxy.newProxyInstance(
TheInterfaceIWant.class.getClassLoader(),
new Class[] { TheInterfaceIWant.class },
new InvocationHandler() {
public Object invoke(
Object proxy, Method method, Object[] args)
throws Throwable {
return aDuck.getClass().getMethod(
method.getName(), method.getParameterTypes()).invoke(aDuck, args);
}
});
Then you can use the wrapper as you would the duck in a dynamically typed language.
if (aDuckWrapper.length() > 0) {
aDuckWrapper.quack();
}
Here's a full length runnable example that prints "Quack" four times using a wrapper:
import java.lang.reflect.*;
public class Duck {
// The interface we use to access the duck typed object.
public interface TheInterfaceIWant {
int length();
void quack();
}
// The underlying instance that does not implement TheInterfaceIWant!
static final class Foo {
public int length() { return 4; }
public void quack() { System.out.println("Quack"); }
}
public static void main(String[] args) throws Exception {
// Create an instance but cast away all useful type info.
final Object aDuck = new Foo();
TheInterfaceIWant aDuckWrapper = (TheInterfaceIWant) Proxy.newProxyInstance(
TheInterfaceIWant.class.getClassLoader(),
new Class[] { TheInterfaceIWant.class },
new InvocationHandler() {
public Object invoke(
Object proxy, Method method, Object[] args)
throws Throwable {
return aDuck.getClass().getMethod(
method.getName(), method.getParameterTypes()).invoke(aDuck, args);
}
});
for (int n = aDuckWrapper.length(); --n >= 0;) {
// Calling aDuck.quack() here would be invalid since its an Object.
aDuckWrapper.quack();
}
}
}
这篇关于用Java模拟鸭子打字的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!