问题
If I write something like def l = [1, 2, 3] as Socket
which is obviously nonsense, I get this:
org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object '[1, 2, 3]' with class 'java.util.ArrayList' to class 'java.net.Socket'
That makes sense.
Now I try something less adventurous:
import java.util.concurrent.BlockingQueue
def l = [1, 2, 3] as BlockingQueue
println l.class
println l
This doesn't throw an exception, and prints the following:
class ArrayList1_groovyProxy
[1, 2, 3]
So what's ArrayList1_groovyProxy
, and why am I able to cast the list to BlockingQueue
without error despite the fact that it fails (l
doesn't end up being an BlockingQueue
instance)?
Edit:
- First of all, I'd like to point out that
def l = [1, 2, 3] as List
works well, and produces a regualr instance ofArrayList
despite the fact that bothList
andBlockingQueue
are interfaces. Second, after Dónal's answer, I tried the following:
def l = [1, 2, 3] as BlockingQueue assert l instanceof BlockingQueue println l.class println l
This printed the following lines and no assertion exceptions were thrown:
class ArrayList1_groovyProxy [1, 2, 3]
However, this line does throw a
MissingMethodException
:l.offer(5)
So the assertion somehow succeeds, but trying to use
l
as a BlockingQueue throws an exception.Third, the same happens if I try
def l = [1, 2, 3] as Map
This code:def l = [1, 2, 3] as Map assert l instanceof Map println l println l.getClass()
produces no errors and prints:
[1, 2, 3] class ArrayList1_groovyProxy
回答1:
It's just Groovy being cute. It's able to see that you're trying to create a collection, but it can't figure out how to construct a BlockingQueue. It's falling back to a proxied ArrayList. If you'd gone with a type declaration on the left side instead of a "def," it would have blown up. Again, it's getting cute because you're using a def. Annoying, isn't it? :)
回答2:
Using x as y
is not casting, it's Coercion (see Section 8.7 of the Groovy Manual).
Coercion does not check type safety when casting.
Also, BlockingQueue
is an interface. I'm not sure why you would cast an object as an interface.
Try running this:
import java.util.concurrent.LinkedBlockingQueue
LinkedBlockingQueue l = [1, 2, 3] as LinkedBlockingQueue
println(l instanceof LinkedBlockingQueue)
println(l.class)
println(l.metaClass.methods*.name.sort().unique())
You get:
true
class java.util.concurrent.LinkedBlockingQueue
[add, addAll, clear, contains, containsAll, drainTo, element, equals, getClass, hashCode, isEmpty, iterator, notify, notifyAll, offer, peek, poll, put, remainingCapacity, remove, removeAll, retainAll, size, take, toArray, toString, wait]
You can't have an instance of an interface, so it had no idea what you asked it to do. For instance, try running new BlockingQueue()
, you can't because you can't have an instance of an interface. This is why you cannot cast an Object to be an interface.
回答3:
why am I able to cast the list to BlockingQueue without error despite the fact that it fails (l doesn't end up being an BlockingQueue instance)?
Why are you so sure that l
is not a BlockingQueue
instance? The following (which you can run in the Groovy console) indicates that it is a BlockingQueue
instance:
import java.util.concurrent.BlockingQueue
// this assignment would be impossible if l is not a BlockingQueue
BlockingQueue l = [1, 2, 3] as BlockingQueue
// this assertion would throw an exception if l is not a BlockingQueue
assert l instanceof BlockingQueue
FYI, you can remove a lot of uncertainty about types by defining the types of your variables, rather than using def
.
来源:https://stackoverflow.com/questions/40195462/groovy-def-l-1-2-3-as-blockingqueue