【问题标题】:Java: How to declare that a variable implements an interface?Java:如何声明一个变量实现了一个接口?
【发布时间】:2011-07-05 19:52:56
【问题描述】:

在 Objective-C 中,我可以做到:

id<HTTPRequestDelegate> delegate;

表示delegateid 类型的变量)符合HTTPRequestDelegate 协议(或在Java 中实现HTTPRequestDelegate 接口)。

这样,每当我向delegate 发送由HTTPRequestDelegate 协议定义的消息时,编译器就会知道delegate 会响应。

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

【问题讨论】:

  • 我认为这不是正确的 Objective-C 语法。你的意思是id&lt;HTTPRequestDelegate&gt; delegate;

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


【解决方案1】:

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

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

List<String> myList;

声明一个List&lt;String&gt;类型的变量myList,其中List是一个接口。

你可以用任何实现这个 List 接口的对象来初始化这个变量:

myList = new ArrayList<String>();

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

【讨论】:

    【解决方案2】:
    //Static typing
    HTTPRequestDelegate delegate;
    

    【讨论】:

      【解决方案3】:
      Interface a = new Implementation();
      

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          【解决方案5】:

          我假设委托没有显式实现您想要的接口。

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

          如果这不是您想要的,您可能需要好好反思一下。看看 java.lang.reflect.Proxy 和 InvocationHandler。

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

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

          【讨论】:

            【解决方案6】:

            已经给出的大多数答案都是正确的。如果一个对象实现了一个接口,那么您可以在任何需要实现该接口的地方使用该对象。鉴于 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

            【讨论】:

              【解决方案7】:

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

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

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

              HTTPRequestDelegate delegate;
              

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

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

              但是,如果您使用像id&lt;HTTPRequestDelegate&gt; 这样的类型,那么您很可能只打算使用HTTPRequestDelegate 提供的方法,因此您没有使用任何动态类型功能。所以在 Java 中,只需 HTTPRequestDelegate 就足够了。

              【讨论】:

                【解决方案8】:

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

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

                HTTPRequestDelegate delegate = (HTTPRequestDelegate) ref;
                

                括号中的位是演员表。您现在可以调用delegate 上的方法(在java speak 中传递消息),只要它们在HTTPRequestDelegate 上定义即可。

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

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2019-12-17
                  • 2015-10-23
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-02-14
                  • 2014-04-03
                  • 1970-01-01
                  相关资源
                  最近更新 更多