I am attempting extend a Java class through JavaScript Nashorn and then call method from the super class. Normally this wouldn't be an issue, though I'm overriding a method, that's being called by the super class' constructor.
Below is my code:
const MyClass = Java.type("com.example.MyClass")
const myInstance = new (Java.extend(MyClass, {
myMethod: () => {
const _super = Java.super(myInstance)
_super.doWhatever()
}
}))()
I set the variable myInstance to a new instance of the extended class, which again is being referenced from within the method myMethod, which is being called by the constructor, which causes myInstance to be undefined by the time myMethod is called.
I'm having trouble figuring out a way to fix this issue, without having to extend it through Java, which would cause issues in the environment I work in.
EDIT:
I have no way of changing the Java code within MyClass
"myInstance" is initialized only after JS "new" operation is completed. This JS "new" involves "new object creation" and "Java constructor call" together (from a single invokedynamic).
You mentioned you're calling myMethod from your (super class) constructor. In this case, you're effectively calling script implemented myMethod method override from super class constructor. But script constant myInstance is not yet initialized. So you'll get error when you attempt Java.super on myInstance which is still undefined!
Root of the problem:
It is not a good idea to call overridable Java methods from a constructor. I recommend you to restructure your java code to avoid calling an overridable method from constructor.
Related
I have a class I reuse, often, to the tune of possibly tens of thousands of instances in a given session. It occurred to me that creating all these properties within the constructor may be replication, that is each function is unique in memory and not a pointer, so I created a little test setup:
const testTree = function (){
console.log(this, this.konnichiwa);
}
const testFjord = function (aloha){
return function() {
console.log(this, aloha, this.konnichiwa);
}
}
class Clown extends Object{
constructor(props){
super(props);
const aloha = "Hello!"; //<- Private party
this.konnichiwa = "Also hello I think"; //<- Everyone's invited
this.testWan = () => {
console.log(this, aloha, this.konnichiwa);
}
this.testTree = testTree;
this.testFjord = testFjord(aloha);
}
testToo = () => {
console.log(this, this.konnichiwa);
}
}
//export default Clown; //this is an export in my application, used lots
const test = new Clown();
const otherTest = new Clown();
console.log(test.testWan === otherTest.testWan);
console.log(test.testToo === otherTest.testToo);
console.log(test.testTree === otherTest.testTree);
console.log(test.testFjord === otherTest.testFjord);
test.testWan();
test.testToo();
test.testTree();
test.testFjord();
Part 1
As you can test above, testWan, testToo, and testFjord are all unique per instance, but testTree is not. Is there any way to declare a "pointer"/"reusable function" but inside class constructor?
The issue here with testToo and testTree is that they can't access private vars within the constructor like testWan can. testFjord is a factory and can be passed these, but then the returned function is unique and won't be able to interact well with vars passed into it.
It's very likely not possible - I think it's a catch 22 scope thing - but you may know better. The only recourse I can think of is to add a property to this for each thing I need to use in testTree, but that exposes things I may not want exposed outside of the class.
Part 2
This part only applies if this is a generally consistent behavior, and not something completely unique per-browser. Does the engine hold onto references to things like conditionals (which I suspect are sorta anonymous-function-like behind the scenes) once the constructor has run?
I have a fairly knarly conditional setup I'm not going to shove in the code here. This is entirely within the constructor right now. I suspect that, although not a function declaration itself, it is also not a pointer, but an entirely fresh instance per the 'new' in new Clown. It needs to manipulate some private vars and so per Part 1 I haven't figured out a good way to extract this.
Example, there are references to private vars inside the constructor for exposed functions: aloha above is private but used by public testWan function, and so needs to be held after constructor has executed. Is the entire constructor held for the life of test & otherTest or is the constructor going to be dropped after use and just the reference to aloha held in memory?
In Python3 I can use magic function __new__, which executes before class initialization. This helps me control whether new instance will be created or we will use some instance from cache.
Just a little simplified example:
class Something:
def __new__(..., someArgument):
# was a class with someArgument initialized somewhere before?
# is yes, then:
return CACHE[someArgument]
# if no, then:
CACHE[someArgument] = Something(someArgument)
return CACHE[someArgument]
So, can I the same in ES6? Or how can I control class initializing in other way?
This question is not a duplicate of this one, because I'm asking whether I can find some functionality in JS, while the topic above contains a duscussion about this functionality.
As Justinas commented, you can look up about Javascript Factory.
A Javascript Factory define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
Some places you can look it up:
Factory Method Design Pattern
Factory Functions with ES6
Calling Javascript Factory Method
I Hope it helped!
You can use factory function:
class Test {
}
function getInstance() {
if (!Test._instance) {
Test._instance = new Test();
}
return Test._instance;
}
No, when a (non-derived) constructor is invoked with new (or super()) then an object is already instantiated before any custom code runs1. However, JS lets you overwrite the result value of the expression by returning an object from the constructor. You can write
const CACHE = new Map();
class Something {
constructor(someArgument) {
if (CACHE.has(someArgument)) return CACHE.get(someArgument);
// ^^^^^^
CACHE.set(someArgument, this);
// initialise `this`
}
}
That said, a factory function (or even static method) - as suggested by the other answers - is usually a more sensible solution. You'd put the cache handling code only in the constructor if you absolutely needed to enforce this.
1: it could be intercepted by the construct trap of a proxy, but you normally would not use that.
I have this class which does an internal call to a static method:
export class GeneralHelper extends BaseHelper{
static is(env){
return config.get('env:name') === env;
}
static isProd(){
return GeneralHelper.is('prod');
}
}
Are there any keywords I can use to replace the class name in the line below:
GeneralHelper.is('prod');
In PHP there are self, static etc. Does ES6 provide anything similar to these?
TY.
If you are calling the static function from inside an instance, the right way to refer to the static function of the class is:
this.constructor.functionName();
Call static methods from regular ES6 class methods
It's the same as calling a method on an ordinary object. If you call the GeneralHelper.isProd() method, the GeneralHelper will be available as this in the method, so you can use
class GeneralHelper {
static is(env) { … }
static isProd(){
return this.is('prod');
}
}
This will however not work when the method is passed around as a callback function, just as usual. Also, it might be different from accessing GeneralHelper explicitly when someone inherits isProd from your class and overwrites is, InheritedHelper.isProd() will produce other results.
If you're looking to call static methods from instance methods, see here. Also notice that a class which only defines static methods is an oddball, you may want to use a plain object instead.
Both of the answers here are correct and good, but I wanted to throw in an added detail based on this question title.
When I saw "ES6 - Call static method within a class" it sounded like "call a static method (from a non-static method) within a class". Def not what the initial question asker is asking in the details.
But for anyone who wants to know how to call a static method from a non-static method within a class you can do it like this:
class MyClass {
myNonStaticMethod () {
console.log("I'm not static.")
MyClass.myStaticMethod()
}
static myStaticMethod () {
console.log("hey, I'm static!")
}
}
MyClass.myStaticMethod() // will log "hey, I'm static!"
const me = new MyClass()
me.myNonStaticMethod() // will log "I'm not static" and then "hey, I'm static!"
The idea is that the static method is can be called without creating a new instance of the class. That means you can call it inside of a instance's method the same way you'd call it outside of the instance.
Again, I know that's not what the detail of the question was asking for, but this could be helpful other people.
I have this class which does an internal call to a static method:
export class GeneralHelper extends BaseHelper{
static is(env){
return config.get('env:name') === env;
}
static isProd(){
return GeneralHelper.is('prod');
}
}
Are there any keywords I can use to replace the class name in the line below:
GeneralHelper.is('prod');
In PHP there are self, static etc. Does ES6 provide anything similar to these?
TY.
If you are calling the static function from inside an instance, the right way to refer to the static function of the class is:
this.constructor.functionName();
Call static methods from regular ES6 class methods
It's the same as calling a method on an ordinary object. If you call the GeneralHelper.isProd() method, the GeneralHelper will be available as this in the method, so you can use
class GeneralHelper {
static is(env) { … }
static isProd(){
return this.is('prod');
}
}
This will however not work when the method is passed around as a callback function, just as usual. Also, it might be different from accessing GeneralHelper explicitly when someone inherits isProd from your class and overwrites is, InheritedHelper.isProd() will produce other results.
If you're looking to call static methods from instance methods, see here. Also notice that a class which only defines static methods is an oddball, you may want to use a plain object instead.
Both of the answers here are correct and good, but I wanted to throw in an added detail based on this question title.
When I saw "ES6 - Call static method within a class" it sounded like "call a static method (from a non-static method) within a class". Def not what the initial question asker is asking in the details.
But for anyone who wants to know how to call a static method from a non-static method within a class you can do it like this:
class MyClass {
myNonStaticMethod () {
console.log("I'm not static.")
MyClass.myStaticMethod()
}
static myStaticMethod () {
console.log("hey, I'm static!")
}
}
MyClass.myStaticMethod() // will log "hey, I'm static!"
const me = new MyClass()
me.myNonStaticMethod() // will log "I'm not static" and then "hey, I'm static!"
The idea is that the static method is can be called without creating a new instance of the class. That means you can call it inside of a instance's method the same way you'd call it outside of the instance.
Again, I know that's not what the detail of the question was asking for, but this could be helpful other people.
I am using YUI3's Auto-complete Plugin. Y.Plugin.Autocomplete.
Now I want to extend this plugin and create some very specific plugins. Such as Y.MyModule.TagAutocomplete, Y.MyModule.EmailAutocomplete and so on.
My simple question is, when I am writing initializer method in my subclass do I need to call superclass constructor explicitly or does it happen implicitly ?
If I have to call it what is the syntax ?
I never tried to extend Plugins, but I did extend from Y.Base and it works as documented here: http://yuilibrary.com/yui/docs/base/
In details:
You create a "constructor function". Here you should call superclass constructor:
function MyClass(config) {
// Invoke Base constructor, passing through arguments
MyClass.superclass.constructor.apply(this, arguments);
}
Next, use Y.extend method to make your own class extended from Y.Base (or Y.Plugin.Autocomplete in your case)
Y.extend(MyClass, Y.Base, {
// Prototype methods for your new class
});
Y.Base has a special method called "initializer" - this method executed on each class in hierarcy when you create a new object and you do not need to call parent's initizlizer manually. I think Y.Plugin.Autocomplete has its own "initializer". So jus do following:
Y.extend(MyClass, Y.Plugin.Autocomplete, {
initializer: function(config) {
alert("This initializer called after Y.Plugin.Autocomplete's initializer");
}
});
Last comment from my side: I've never tried to extend Y.Plugin.Autocomplete, my be there is something under the hood in Autocomplete realization. Try it!