Java:如何声明变量实现接口?
在 Objective-C 中,我可以这样做:
id<HTTPRequestDelegate> delegate;
说 delegate
(id
类型的变量)符合 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
Java 中不存在鸭子类型。如果一个类实现了一个接口,那么它必须声明该接口已实现。仅仅拥有与接口中的方法具有相同签名的方法是不够的。
不过,接口是一种类型,您可以声明这种类型的变量。例如:
声明一个
List
类型的变量myList
,其中List
是一个接口。您可以使用实现此 List 接口的任何对象来初始化此变量:
但是 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:
declares a variable
myList
of typeList<String>
, whereList
is an interface.You may initialize this variable with any object implementing this List interface:
But then
ArrayList
must declare that it implements theList
interface (which it does).Java 没有鸭子类型的概念。您必须将实例强制转换为已知类型。
Java has no concept of duck typing. You must cast the instance to a known type.
我假设委托没有显式实现您想要的接口。
您可以创建一个新类来实现该接口并扩展您想要的实现类(或者具有实现类并显式调用接口中的适当方法)。
如果这不是您想要的,您可能需要进行一次健康的反思。看一下 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.
大多数已经给出的答案都是正确的。如果一个对象实现了一个接口,那么您可以在任何需要该接口的实现的地方使用该对象。鉴于 Java 的强类型系统,这是最自然的方法。
为了与
List
/ArrayList
示例保持一致,您可以创建一个ArrayList
对象,然后在List
的任何位置使用它> 是必需的 - 或者,基于其他实现的接口,Serialized
、Cloneable
、Iterable
、Collection
、或随机访问
。考虑到超类,ArrayList
的实例可以用作AbstractList
、AbstractCollection
或java.lang.Object
>。反射可以与动态代理对象一起使用,通过正确的方法将对象楔入鸭子服装中。这将类型检查转移到运行时,并且通常有更好的理由使用正常的类型系统而不是反对它。
因为听起来很有趣,这里有一个将非 Duck 包装在代理对象中的示例。
无论如何,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 anArrayList
object and then use it anywhere aList
is required -- or, based on the other implemented interfaces,Serializable
,Cloneable
,Iterable
,Collection
, orRandomAccess
. Considering superclasses, an instance ofArrayList
can be used as anAbstractList
,AbstractCollection
, or ajava.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.
For what it's worth, IBM developerWorks also has a good article on the topic of dynamic proxies.
在 Objective-C 中,类型由两部分组成: 1) 类指针类型(例如
NSObject *
、NSString *
等);这也可以是 id,它是一种特殊类型,可以接受任何对象指针并禁用调用方法的静态类型编译器警告; 2) 可选地,对象遵循的一个或多个协议(类似于 Java 中的接口)(例如
)在 Java 中,引用类型可以是类或接口姓名。 (您只能选择一个。)类和接口之间没有太大的区别。
在您的情况下,您的对象指针类型是
id
,它不表示任何信息,并且您指定了一个接口HTTPRequestDelegate
。这可以在 Java 中等效地表示为:如果您指定了多个协议,或者指定了实际的类指针类型加上一个或多个协议,那么您的类型就是“交集类型”,即您指定的多个类型的交集。在这种情况下,会更困难,因为在 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 beid
, 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 asIf 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 byHTTPRequestDelegate
anyway, so you are not using any of the dynamic typing abilities. So in Java just HTTPRequestDelegate will suffice.我认为这里有很多术语需要解释。 Java 不允许您拥有原始指针,而只能拥有具有类型的引用。
不管怎样,假设您有一个对实现
HTTPRequestDelegate
的实例的引用。您可以像这样进行强制转换:括号中的位是强制转换。现在,您可以调用
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: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 onHTTPRequestDelegate
.The other way Java programmers do duck typing type stuff is refection, but if you know the interface, casing is the way to go.