UPDATE: Based on various feedback, I'm significantly updating my question so much so that it is different from (though still related to) the original one. I don't know what the proper protocol is for this on StackOverflow, so I'm sorry if I'm doing the wrong thing.
I’m working in ServiceNow, a popular Software-as-a-Service platform implemented in Java that executes customer JavaScript code using the Rhino JavaScript engine. One of their APIs is GlideRecord, which is the API customers use to do CRUD operations against the MySQL database where all data is stored on the platform.
When I access a particular field value through the GlideRecord API, the field value is provided in the form of an object called GlideElement:
// JavaScript code
var gr = new GlideRecord('incident');
gr.setLimit(1);
gr.query();
gr.next();
var ge = gr.short_description; // GlideElement object
Through the magic of the Rhino engine, both GlideRecord and GlideElement are not JavaScript objects but Java objects somehow shared into the JavaScript runtime. The weird thing about the GlideElement object is it seems to somehow be an instance of java.lang.String:
gs.print(ge instanceof GlideElement); // true
gs.print(ge instanceof Packages.java.lang.String); // true
(The Packages
object is supplied by Rhino as a way to access things like java.lang.String
. This looks fishy, but as far as I know it is the real java.lang.String
from Java, no funny business.)
My original question asked how something could be an instance of two classes and I now understand that's an ordinary thing enabled by polymorphism (subclassing and interface implementation). However, the java.lang.String class is a final
class, so it shouldn't be subclassable or implementable by any other class.
Unfortunately the code above is the entirety of what I can provide because the SaaS platform's Java code is not open source, so I can't (and no one can) see how GlideRecord and GlideElement are implemented under the hood. Without access to the source code, what can we know or speculate about how this could have been implemented in either vanilla Java or some arcane witch magic enabled by the Rhino engine?
The code above can be executed if you have access to any ServiceNow instance. You can register for free and spin up a “personal developer instance” of ServiceNow at https://developer.servicenow.com. Once you have an instance you can navigate to All › System Definition › Scripts - Background to paste and execute arbitrary JavaScript code.
So, the first thing that we need to establish here is that you're not using the Java instanceof
-- you're using the JavaScript instanceof
operator.
So the operator you're using is based on the JavaScript prototype chain, and will not return the same results as the Java instanceof
.
So what's happening with the JavaScript instanceof
? The bad news is, the prototype chain is not well-defined for Java objects in the Rhino environment, because Java objects in Rhino are ECMAScript "host objects," which do not need to obey ordinary JavaScript semantics. ("Host objects" with which you might be more familiar are things like window
and document
in the browser environment, which do not behave like ordinary JavaScript objects in a variety of ways.)
Thinking about Java objects specifically, Java does not have prototype-based inheritance, so Java objects do not have prototypes like JavaScript objects do, and won't behave exactly like JavaScript objects. To give an example, there's no Packages.java.lang.Object.prototype
even though
there's a new Packages.java.lang.Object()
, and you certainly can't assign properties to Packages.java.lang.Object.prototype
and have new methods show up throughout the JVM, or even throughout Rhino.
So you'd really have to start digging into source code if you wanted to figure out how it works, and it might well be version-dependent. I don't know exactly what version of Rhino is bundled with ServiceNow (you might be able to hack into the Rhino subsystem and do something like Context.getCurrentContext().getImplementationVersion()
in a script), but you're going to rapidly get into esoterica if you start trying to figure out how the JavaScript prototype chain is modeled for Java objects in Rhino. Here's a Stack Overflow question which discusses it for a user-defined JavaScript object implemented in Java. And here's someone trying to use a Java object as a JavaScript prototype.
Anyway, without learning more about your use case, it's hard to say what parts of this will matter for you. But if you're just curious about the wacky result you observed, the basic answer to your question is that the Java type system isn't the basis for the instanceof
you're using.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With