I understand how protocols can be used to introduce new behaviour to existing classes, bu开发者_如何学JAVAt is it possible for them (or any other Clojure mechanism) to introduce state to existing classes? More specifically, I would like to be able to associate a map with instances of a class that comes from a 3rd-party library.
Protocols are conceptually similar in Java Interfaces in that they don't concern themselves with state or representation at all, so I'm pretty sure you can't do it that way unless you store the state outside the object itself. You can however use the various other ways of extending (subclassing) classes in Clojure to do this, e.g. using a proxy
or with gen-class
. (see
http://clojure.org/java_interop#Java%20Interop-Implementing%20Interfaces%20and%20Extending%20Classes)
You can create a protocol with set-state
and get-state
functions. Then extend them to the classes you want, with an implementation built around a hashmap of some kind. You can't store the state in the foreign objects, but you can have your functions share a ref of a hashtable keyed by object. I think this solution may have a number of problems, like how do you detect when an object will be GCed and its state needs to be cleared as well? You can use a WeakReference or something, but it isn't trivial.
First off, protocols are interface definitions, and generally, you don't want to specify state in an interface. You generally want to put state in an implementation of an interface, see below.
For most of the clojure constructs that allow you to implement interfaces or extend classes the sanest way - especially if you don't own the class - is to use closures to capture state. You can capture mutable types to implement mutable state (though that's probably something you want to avoid if you can).
An immutable example from http://clojure.github.com/clojure/clojure.core-api.html#clojure.core/reify
(str (let [f "foo"]
(reify Object
(toString [this] f))))
Note that f here could be a ref or a var or an atom instead of a string if you want mutable state.
EDIT: as noticed in the comments, I might not have made this completely clear: you can use defprotocol to define a function-based interface, and then use reify to create instances of that protocol to capture state.
EDIT 2: Sorry for making this confusing. This code won't actually work for existing classes, because reify does't support it. Proxy probably works as an alternative, though the documentation doesn't flat-out states that function names in a protocol map 1-1 to interface methods.
You can store state. You just need to use an atom or a ref and reference and dereference that atom or ref in your get and set methods
精彩评论