简体   繁体   English

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

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

In Objective-C, I could do:在 Objective-C 中,我可以这样做:

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).表示delegate (类型为id的变量)符合HTTPRequestDelegate协议(或实现了 Java 中的HTTPRequestDelegate接口)。

That way, whenever I send a message defined by the HTTPRequestDelegate protocol to delegate , the compiler understands that delegate responds.这样,每当我将HTTPRequestDelegate协议定义的消息发送给delegate时,编译器就会知道该delegate会响应。

How do I do this, ie, duck typing / dynamic typing, in Java?在 Java 中,我该如何做到这一点,即鸭式打字/动态打字?

Duck typing doesn't exist in Java. Java 中不存在鸭子类型。 If a class implements an interface, it must declare that this interface is implemented.如果一个 class 实现了一个接口,它必须声明这个接口被实现。 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.声明一个List<String>类型的变量myList ,其中List是一个接口。

You may initialize this variable with any object implementing this List interface:您可以使用任何实现此 List 接口的 object 来初始化此变量:

myList = new ArrayList<String>();

But then ArrayList must declare that it implements the List interface (which it does).但随后ArrayList必须声明它实现了List接口(它确实如此)。

//Static typing
HTTPRequestDelegate delegate;
Interface a = new Implementation();

Java has no concept of duck typing. Java 没有鸭子类型的概念。 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).您可以创建一个新的 class 来实现接口并扩展您想要的实现 class (或者具有实现 class 并在接口中显式调用适当的方法)。

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.看看 java.lang.reflect.Proxy 和 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.如果您正在寻找一种简写方式来避免使用组合显式实现接口的方法,那么 Java 并没有真正为此提供语法支持。 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.如果您确实想要 go 反射重的方式(不建议过度输入),请查看 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.如果 object 实现了一个接口,那么您可以在需要实现该接口的任何地方使用该 object。 This is the most natural approach given Java's strong typing system.鉴于 Java 的强类型系统,这是最自然的方法。

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 .为了与List / ArrayList的示例保持一致,您可以创建一个ArrayList object 然后在需要List的任何地方使用它——或者,基于Collection Serializable Cloneable Iterable RandomAccess Considering superclasses, an instance of ArrayList can be used as an AbstractList , AbstractCollection , or a java.lang.Object .考虑到超类, ArrayList的实例可以用作AbstractListAbstractCollectionjava.lang.Object

Reflection can be used, along with dynamic proxy objects, to wedge an object with the correct methods into a duck costume.反射可以与动态代理对象一起使用,将具有正确方法的 object 楔入鸭子服装中。 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.因为这听起来很有趣,所以这里有一个将非 Duck 包装在代理 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.值得一提的是,IBM developerWorks 也有一篇关于动态代理主题的好文章

In Objective-C, the type consists of two parts: 1) An class pointer type (eg NSObject * , NSString * , etc);在 Objective-C 中,类型由两部分组成: 1) 一个 class 指针类型(例如NSObject *NSString *等); 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;这也可以是id ,这是一种特殊类型,可以接受任何 object 指针并禁用 static 类型的编译器对调用方法的警告; and 2) optionally, one or more protocols (which are like interfaces in Java) that the object conforms to (eg <NSCopying, NSCoding> )和 2) 可选地,object 符合的一个或多个协议(类似于 Java 中的接口)(例如<NSCopying, NSCoding>

In Java, a reference type is either a class or interface name.在 Java 中,引用类型是 class 或接口名称。 (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 .在您的情况下,您的 object 指针类型是id ,它不表示任何信息,并且您指定了一个接口HTTPRequestDelegate This can be equivalently expressed in Java as这可以在 Java 中等价地表示为

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.如果您指定了多个协议,或者您指定了一个实际的 class 指针类型加上一个或多个协议,那么您的类型是“交集类型”,即您指定的多种类型的交集。 In that case, it would be harder because there is no simple way of expressing intersection types in Java.在这种情况下,它会更难,因为在 Java 中没有简单的方法来表达交集类型。 (Although intersection types can be specified in generic type bounds, eg class Foo<T extends Collection & Comparable & CharSequence> ) (虽然可以在泛型类型边界中指定交集类型,例如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 (ie 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). Other than that, the only other difference between Objective-C and Java is that in Objective-C, you can send any message (ie call any method) on an object pointer and it is allowed, even if the static type of the variable does not indicate that it受支持(如果您使用实际的 class 指针类型,编译器将发出警告;如果您使用id它不会发出警告)。 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.而在 Java 中,您只能在编译时调用已知由 static 类型支持的方法。

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.但是,如果您使用像id<HTTPRequestDelegate>这样的类型,那么您很可能只打算使用HTTPRequestDelegate提供的方法,因此您没有使用任何动态类型功能。 So in Java just HTTPRequestDelegate will suffice.所以在 Java 中,只需 HTTPRequestDelegate 就足够了。

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. Java 不让你有一个原始指针,只有一个引用,它有一个类型。

Anyway, say you have a reference to an instance that you know implements HTTPRequestDelegate .无论如何,假设您引用了一个您知道实现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 .您现在可以调用delegate上的方法(在 java 发言中传递消息),只要它们是在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.另一种方式 Java 程序员做鸭子打字类型的东西是反射,但如果你知道接口,大小写是 go 的方式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM