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.
2
Answers
Yes, and it happens all the time. No magic or weird constructs necessary. Just
extends
orimplements
.Could be; but you don’t need ‘rhino javascript engine magic’ to explain the notion that a single object is
instanceof
more than one type, because by definition every java object is except literallynew Object()
:instanceof
does not check ‘is the expression an instance of this exact class’. Name notwithstanding.it checks ‘is the expression an instance of this exact type or any class that is a subtype of it‘.
The ‘simple’ (and incorrect) view (that it checks only if it is the exact class named) sounds nice but is almost entirely useless. It would imply that e.g. doing
instanceof Number
, orinstanceof List
is useless as it would necessarily returnfalse
(Number and List are interfaces / abstract classes; it’s impossible to create an object that is specifically an instance of them;new Number()
is not legal, nor isnew List()
). Lots of design has all the actual concrete types as non-public. You craft them via factory constructors and the only public type available is a supertype of some sort.instanceof
would be useless there.If
x instanceof Type
returnstrue
, then you can castx
to Type and it will work. That’s a useful operation and is whatinstanceof
does.Remember –
class Y extends X
means that Y is designed as: Take everything thatclass X{}
defines. Every field, every method. Y has all of those things. Now Y can define even more things that Y has, but, it can’t take things away.In other words, given
class Y extends X
, the whole point of the exercise is that you can treat instances of Y as if they were an X. Because they are.The right terminology is is a.
class Integer extends Number
means "any instance of Integer is a Number". With that terminology, it’s obvious whyInteger i = 5; System.out.println(i instanceof Number);
printstrue
. Becausei
is aNumber
.Do you have a pet dog? Let’s say you do.
Rover is a dog.
Rover is an animal.
rover instanceof Animal
istrue
. That should be obvious, and logical.rover instanceof Dog
is alsotrue
. Hopefully that makes inherent sense.But I want the specific class / check the specific impl!
No problem. You’re looking for
.getClass()
instead ofinstanceof
:So, the first thing that we need to establish here is that you’re not using the Java
instanceof
— you’re using the JavaScriptinstanceof
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 likewindow
anddocument
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 thoughthere’s a
new Packages.java.lang.Object()
, and you certainly can’t assign properties toPackages.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.