开发者

Java: How to declare that a variable implements an interface?

In Objective-C, I could do:

id<HTTPRequestDelegate> delegate;

to say that delegate (a variable of type id) conforms to the HTTPRe开发者_C百科questDelegate protocol (or implements the HTTPRequestDelegate interface in Java speak).

That way, whenever I send a message defined by the HTTPRequestDelegate protocol to delegate, the compiler understands that delegate responds.

How do I do this, i.e., duck typing / dynamic typing, in Java?


Duck typing doesn't exist in Java. If a class implements an interface, it must declare that this interface is implemented. It isn't sufficient just to have methods with the same signature as the ones in the interface.

An interface is a type, though, and you may declare a variable of this type. For example:

List<String> myList;

declares a variable myList of type List<String>, where List is an interface.

You may initialize this variable with any object implementing this List interface:

myList = new ArrayList<String>();

But then ArrayList must declare that it implements the List interface (which it does).


//Static typing
HTTPRequestDelegate delegate;


Interface a = new Implementation();


Java has no concept of duck typing. You must cast the instance to a known type.


I'm assuming then that delegate doesn't explicitly implement the interface you want.

You could make a new class that implements the interface and extends the implementing class you want (or has the implementing class and explicitly calls the appropriate method in the interface).

If this isn't what you want, you might be in for a healthy dose of reflection. Take a look at java.lang.reflect.Proxy and InvocationHandler.

If you are looking for a shorthand to avoid explicitly implementing methods for an interface using composition, Java doesn't really provide syntactic support for this. You'll have to be explicit.

If you do want to go the reflection-heavy way (not recommended over extra typing), take a look at Mockito.


Most of the answers given already are correct. If an object implements an interface, then you can use that object anywhere an implementation of that interface is needed. This is the most natural approach given Java's strong typing system.

To keep with the example of List/ArrayList, you can create an ArrayList object and then use it anywhere a List is required -- or, based on the other implemented interfaces, Serializable, Cloneable, Iterable, Collection, or RandomAccess. Considering superclasses, an instance of ArrayList can be used as an AbstractList, AbstractCollection, or a java.lang.Object.

Reflection can be used, along with dynamic proxy objects, to wedge an object with the correct methods into a duck costume. That shifts the type checking to runtime, and there are usually far better reasons to work with the normal typing system than against it.

Because it sounded like fun, here an example of wrapping a non-Duck in a proxy object.

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class DuckDemo {
    public static Duck getDuckProxy(final Object duckLike) {
        final InvocationHandler invocationHandler = new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Class<?> actualClass = duckLike.getClass();
                String methodName = method.getName();
                Class[] parameterTypes = method.getParameterTypes();

                Method requested = actualClass.getDeclaredMethod (methodName, parameterTypes);

                return requested.invoke(duckLike, args);
            }
        };

        final ClassLoader originalObjectClassLoader = duckLike.getClass().getClassLoader();
        Duck woodenDuck = (Duck) Proxy.newProxyInstance(
                originalObjectClassLoader,
                new Class[] { Duck.class },
                invocationHandler
        );

        return woodenDuck;
    }

    private interface Duck {
        void quack();
    };

    public static void makeItQuack (Duck duck) {
        duck.quack();
    }

    public static void main (String args[]) {
        Object quacksLikeADuck = new Object() {
            void quack() {
                System.out.println ("Quack!");
            }
        };

        // Does not compile -- makeItQuack(DuckDemo.Duck) [...] cannot be applied to (java.lang.Object)
        // makeItQuack (quacksLikeADuck);

        // Runtime java.lang.ClassCastException: [...] cannot be cast to GenericProxyFactory$Duck
        // makeItQuack ((Duck)quacksLikeADuck);

        Duck d = getDuckProxy(quacksLikeADuck);

        makeItQuack (d);
    }
}

For what it's worth, IBM developerWorks also has a good article on the topic of dynamic proxies.


In Objective-C, the type consists of two parts: 1) An class pointer type (e.g. NSObject *, NSString *, etc); this could also be id, which is a special type that can accept any object pointer and disables static type compiler warnings for calling methods; and 2) optionally, one or more protocols (which are like interfaces in Java) that the object conforms to (e.g. <NSCopying, NSCoding>)

In Java, a reference type is either a class or interface name. (You can only pick one.) There is not so much separation between classes and interfaces.

In your case, your object pointer type is id, which expresses no information, and you specified one interface, HTTPRequestDelegate. This can be equivalently expressed in Java as

HTTPRequestDelegate delegate;

If you had specified more than one protocol, or you specified an actual class pointer type plus one or more protocols, then your type is an "intersection type", the intersection of the multiple types you specified. In that case, it would be harder because there is no simple way of expressing intersection types in Java. (Although intersection types can be specified in generic type bounds, e.g. class Foo<T extends Collection & Comparable & CharSequence>)

Other than that, the only other difference between Objective-C and Java is that in Objective-C, you can send any message (i.e. call any method) on an object pointer and it is allowed, even if the static type of the variable does not indicate that it is supported (the compiler will give a warning if you use an actual class pointer type; if you use id it will not give a warning). I guess this is the dynamic typing you're talking about. Whereas in Java, you can only call methods that are known to be supported by the static type at compile time.

But if you're using a type like id<HTTPRequestDelegate>, then chances are that you only intend to use the methods provided by HTTPRequestDelegate anyway, so you are not using any of the dynamic typing abilities. So in Java just HTTPRequestDelegate will suffice.


I think there's a lot of terminology to unpack here. Java doesn't let you have a raw pointer, only a reference, which has a type.

Anyway, say you have a reference to an instance that you know implements HTTPRequestDelegate. You can cast it, like so:

HTTPRequestDelegate delegate = (HTTPRequestDelegate) ref;

The bit in the parentheses is the cast. You can now call methods on delegate (pass messages in java speak) to your hearts content as long as they are defined on HTTPRequestDelegate.

The other way Java programmers do duck typing type stuff is refection, but if you know the interface, casing is the way to go.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜