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

发布于 2024-11-18 20:39:04 字数 477 浏览 13 评论 0原文

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

id<HTTPRequestDelegate> delegate;

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

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

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

In Objective-C, I could do:

id<HTTPRequestDelegate> delegate;

to say that delegate (a variable of type id) conforms to the HTTPRequestDelegate 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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

身边 2024-11-25 20:39:04

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

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

List<String> myList;

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

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

myList = new ArrayList<String>();

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

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).

來不及說愛妳 2024-11-25 20:39:04
//Static typing
HTTPRequestDelegate delegate;
//Static typing
HTTPRequestDelegate delegate;
∞梦里开花 2024-11-25 20:39:04
Interface a = new Implementation();
Interface a = new Implementation();
扛起拖把扫天下 2024-11-25 20:39:04

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

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

国粹 2024-11-25 20:39:04

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

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

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

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

如果您确实想采用反射密集型方式(不推荐额外打字),请看看 Mockito。

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.

内心旳酸楚 2024-11-25 20:39:04

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

为了与 List/ArrayList 示例保持一致,您可以创建一个 ArrayList 对象,然后在 List 的任何位置使用它> 是必需的 - 或者,基于其他实现的接口,SerializedCloneableIterableCollection、或随机访问。考虑到超类,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);
    }
}

无论如何,IBMdeveloperWorks 还有一篇好文章 关于动态代理的主题。

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.

本王不退位尔等都是臣 2024-11-25 20:39:04

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

在 Java 中,引用类型可以是类或接口姓名。 (您只能选择一个。)类和接口之间没有太大的区别。

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

HTTPRequestDelegate delegate;

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

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

但是,如果您使用像 id 这样的类型,那么您很可能只想使用 HTTPRequestDelegate 提供的方法,因此您不会使用任何动态打字能力。所以在 Java 中只需 HTTPRequestDelegate 就足够了。

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.

木有鱼丸 2024-11-25 20:39:04

我认为这里有很多术语需要解释。 Java 不允许您拥有原始指针,而只能拥有具有类型的引用。

不管怎样,假设您有一个对实现 HTTPRequestDelegate 的实例的引用。您可以像这样进行强制转换:

HTTPRequestDelegate delegate = (HTTPRequestDelegate) ref;

括号中的位是强制转换。现在,您可以调用 delegate 上的方法(用 java 语言传递消息),只要它们是在 HTTPRequestDelegate 上定义的即可。

Java 程序员处理鸭子类型的另一种方法是反射,但如果您了解接口,则可以使用套管。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文