3

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

id<HTTPRequestDelegate> delegate;

delegate(类型的变量id)符合HTTPRequestDelegate协议(或HTTPRequestDelegate用Java语言实现接口)。

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

我如何在 Java 中做到这一点,即鸭子类型/动态类型?

4

8 回答 8

3

Java 中不存在鸭子类型。如果一个类实现了一个接口,它必须声明这个接口已经实现。仅仅拥有与接口中的方法具有相同签名的方法是不够的。

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

List<String> myList;

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

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

myList = new ArrayList<String>();

但随后ArrayList必须声明它实现了List接口(它确实如此)。

于 2011-07-05T20:04:23.027 回答
2
//Static typing
HTTPRequestDelegate delegate;
于 2011-07-05T19:57:45.550 回答
1
Interface a = new Implementation();
于 2011-07-05T19:54:57.063 回答
0

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

于 2011-07-05T20:03:50.473 回答
0

在 Objective-C 中,类型由两部分组成: 1) 类指针类型(例如NSObject *NSString *等);this 也可以是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 就足够了。

于 2011-07-06T11:55:49.697 回答
0

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

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

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

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

如果您确实想采用大量反射的方式(不建议过度输入),请查看 Mockito。

于 2011-07-05T20:12:28.683 回答
0

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

为了与List/的示例保持一致ArrayList,您可以创建一个ArrayList对象,然后在需要 a 的任何地方使用它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 也有一篇关于动态代理主题的好文章

于 2011-07-06T05:19:28.517 回答
-1

我认为这里有很多术语要解开。Java不允许你有一个原始指针,只有一个引用,它有一个类型。

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

HTTPRequestDelegate delegate = (HTTPRequestDelegate) ref;

括号中的位是演员表。只要delegateHTTPRequestDelegate.

Java 程序员做鸭式类型的另一种方法是反射,但如果你知道接口,那么大小写就是要走的路。

于 2011-07-05T19:58:40.113 回答