Java:如何声明变量实现接口?

时间:2011-07-05 19:52:56

标签: java generics interface duck-typing dynamic-typing

在Objective-C中,我可以这样做:

id<HTTPRequestDelegate> delegate;

delegate(类型为id的变量)符合HTTPRequestDelegate协议(或在Java中实现HTTPRequestDelegate接口)。

这样,每当我将HTTPRequestDelegate协议定义的消息发送到delegate时,编译器就会理解delegate响应。

我如何在Java中执行此操作,即duck typing /动态类型?

8 个答案:

答案 0 :(得分:3)

Java中不存在Duck类型。如果类实现了接口,则必须声明该接口已实现。仅使用与接口中的方法具有相同签名的方法是不够的。

接口是一种类型,您可以声明此类型的变量。例如:

List<String> myList;

声明myList类型的变量List<String>,其中List是一个接口。

您可以使用实现此List接口的任何对象初始化此变量:

myList = new ArrayList<String>();

但是ArrayList必须声明它实现了List接口(它做了)。

答案 1 :(得分:2)

//Static typing
HTTPRequestDelegate delegate;

答案 2 :(得分:1)

Interface a = new Implementation();

答案 3 :(得分:0)

Java没有鸭子打字的概念。您必须将实例强制转换为已知类型。

答案 4 :(得分:0)

我假设那个委托没有明确地实现你想要的接口。

您可以创建一个实现该接口的新类,并扩展您想要的实现类(或者具有实现类并在接口中显式调用相应的方法)。

如果这不是您想要的,那么您可能需要进行健康的反射。看一下java.lang.reflect.Proxy和InvocationHandler。

如果您正在寻找一种速记来避免使用组合显式实现接口的方法,那么Java并不真正为此提供语法支持。你必须明确。

如果您确实想要采用反射方式(不建议使用额外打字),请查看Mockito。

答案 5 :(得分:0)

已经给出的大部分答案都是正确的。如果对象实现了接口,那么您可以在需要该接口的实现的任何地方使用该对象。鉴于Java强大的打字系统,这是最自然的方法。

要与List / ArrayList的示例保持一致,您可以创建一个ArrayList对象,然后在需要List的任何地方使用它 - 或者,基于其他实现的接口SerializableCloneableIterableCollectionRandomAccess。考虑到超类,ArrayList的实例可以用作AbstractListAbstractCollectionjava.lang.Object

可以使用反射以及动态代理对象将具有正确方法的对象楔入鸭子服装中。这会将类型检查转移到运行时,通常使用正常的打字系统比使用它更好。

因为它听起来很有趣,这里有一个将非Duck包装在代理对象中的例子。

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

对于它的价值,IBM developerWorks也有关于动态代理主题的good article

答案 6 :(得分:0)

在Objective-C中,类型由两部分组成:1)类指针类型(例如NSObject *NSString *等);这也可以是id,这是一种特殊类型,可以接受任何对象指针并禁用调用方法的静态类型编译器警告; 2)可选地,对象符合的一个或多个协议(类似于Java中的接口)(例如<NSCopying, NSCoding>

在Java中,引用类型是类或接口名称。 (你只能选择一个。)类和接口之间没有那么多的分离。

在您的情况下,您的对象指针类型为id,它不表示任何信息,您指定了一个接口HTTPRequestDelegate。这可以在Java中等效地表示为

HTTPRequestDelegate delegate;

如果您指定了多个协议,或者您指定了实际的类指针类型以及一个或多个协议,那么您的类型是“交集类型”,即您指定的多个类型的交集。在这种情况下,它会更难,因为在Java中没有简单的表达交集类型的方法。 (尽管可以在泛型类型边界中指定交集类型,例如class Foo<T extends Collection & Comparable & CharSequence>

除此之外,Objective-C和Java之间的唯一区别是,在Objective-C中,您可以在对象指针上发送任何消息(即调用任何方法)并且允许它,即使静态类型为该变量并不表示它受支持(如果使用实际的类指针类型,编译器将发出警告;如果使用id,则不会发出警告)。我想这是你正在谈论的动态类型。而在Java中,您只能在编译时调用已知静态类型支持的方法。

但是如果你使用类似id<HTTPRequestDelegate>的类型,那么你可能只打算使用HTTPRequestDelegate提供的方法,所以你没有使用任何动态类型化的能力。所以在Java中只需HTTPRequestDelegate即可。

答案 7 :(得分:-1)

我认为在这里解压缩有很多术语。 Java不会让你有一个原始指针,只有一个引用,它有一个类型。

无论如何,假设你有一个你知道实现HTTPRequestDelegate的实例的引用。您可以像以下一样投射它:

HTTPRequestDelegate delegate = (HTTPRequestDelegate) ref;

括号中的位是强制转换。现在,您可以在delegate上定义HTTPRequestDelegate上的方法(将java中的消息传递给您的内容),只要它们在{{1}}上定义即可。

Java程序员做鸭子打字类型的另一种方式是反思,但如果你知道界面,那么套管就是你要走的路。