Simulating duck typing in Java
Asked Answered
L

3

10

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!

Lynsey answered 1/12, 2010 at 23:50 Comment(14)
is this an academic exercise only?Fiertz
Do you know java reflection? It allows you access any field any method through names but it is rather complicated if you want to it for every commands in your program.Quetzalcoatl
Reflection is the way to go, specially if you don't know what to call until runtime.Rebak
What about if I were to know the property name at compile time? This is a variant of the problem.Lynsey
How about JRuby or Groovy, etc, themselves? I.e. use them as a proxy.Shyster
@pst Sounds interesting. Can you elaborate with a Groovy code example? Perhaps as an answer?Lynsey
@McKAMEY : If you need it at compile time, you can generate code to satisfy a given interface( and you can even create the interface ) that's more like "Structural typing" ( that's what I do )Rebak
@McKAMEY, are you familiar with JSR 223 which adds scripting language support to the JVM? groovy.codehaus.org/JSR+223+Scripting+with+Groovy shows how it can be done with groovy. Alternatively, I think groovyc now supports compilation of a mixed java/groovy app : groovyland.wordpress.com/2007/06/06/…Lather
@McKAMEY About your edit: Groovy et at use reflection. Even scala for structural type ( codemonkeyism.com/scala-goodness-structural-typing) use reflectionRebak
@Mike-Samuel that was what got me down this path originally. I'd rather not have it parse "script" from strings but I figured there was probably existing support classes. I was wondering if I could use some of the javax.script.* classes to do this and have it "just work".Lynsey
@Rebak Interesting. I noticed that in the Groovy source. They do make extensive use caching to mitigate the perf issues of reflection: svn.codehaus.org/groovy/trunk/groovy/groovy-core/src/main/org/…Lynsey
@McKAMEY: What are you doing anyway? Look very interesting and probably is somehow related to something I'm working on.Rebak
@Rebak finishing up binding code in my dual-side templating engine duelengine.orgLynsey
@McKAMEY Wow!, looks very interesting, I'll take a look at it next weekedn. So, if you are to generate code on the fly, I would suggest to generate the interface and a class implementing the interface to simultate this duck typing.Rebak
L
10

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();
    }
  }
}
Lather answered 2/12, 2010 at 0:39 Comment(6)
Interesting. So ultimately performance would be roughly the same between Proxy and reflection/Inspector since they all use reflection. But this might be nice to us as a type adaptor from one interface to another. Thanks!Lynsey
@McKAMEY, Yeah. Unfortunately, there is a bit more overhead. Method.getParameterTypes() requires an array allocation and copy for all non-parameterless methods. If that turns out to be significant overhead, some skillful memoizing should be able to reduce that once you have some data on usage patterns.Lather
More links for this technique thinking-in-code.blogspot.com/2008/11/… and download.oracle.com/javase/1.5.0/docs/guide/reflection/…Lynsey
The biggest problem with this technique (besides the overhead) is that you still must back it all with interfaces. It is dynamic in that the classes you are coercing don't need to inherit from those interfaces but the code that uses it requires the interface. That's the biggest departure from true duck-typing IMO. Works great as an adapter though when you have an interface and a class that doesn't implement it. You could even remap the method names before you looked up the actual method.Lynsey
Duck type is for dynamic typed languages, for static typed languages you should take a look at "structural typing" There's an implementation that does this ( whiteoak.sourceforge.net ) . Java however doesn't support structural typing.Rebak
@McKAMEY, I agree that it may not be the most convenient solution, <nit-picking>but it is duck typing. IIRC, Duck typing is when you define the contract for a program element in terms of the operators provided, instead of in terms of nominal types or RTTI. That one has to bundle the operators in an interface and go through a wrapping step is orthogonal to the question of whether it is duck typing.</nit-picking>Lather
L
1

Another method that I just came across which leverages (abuses?) type erasure is kind of interesting:

http://rickyclarkson.blogspot.com/2006/07/duck-typing-in-java-and-no-reflection.html

I'm not sure that I buy that this is much different from simply using the interfaces directly but perhaps it is useful to someone else.

Lynsey answered 1/12, 2010 at 23:51 Comment(1)
Related to this: #4289442Rebak
E
0

Take a look at the methods of java.lang.Class and at the reflection API: java.lang.reflect.*

Earleenearlene answered 1/12, 2010 at 23:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.