title: Nashorn, Java collections, How to implement equals and hashCode in pure javascript
body: <p>I’ve faced with the following problem. I’d like to use <code>java.util.HashMap</code> and <code>java.util.PriorityQueue</code> in <strong>Nashorn</strong> script, where I need to use particular custom object as a <em>key in the HashMap</em>, and also use <code>HashMap.containsKey()</code> to check if there is a key in the Map (another option is to check if the object in a Collection.contains(Object o)).</p>
  
  <p>So, obviously, I need to implement equals and hashCode in my object basing on some field values.</p>
  
  <p>For example:</p>
  
  <ol>
  <li><p>Trying to use JavaScript. Doesn’t work due to JavaScript doesn’t have those methods. Please see <strong>Sample 1</strong> and <strong>Sample 2</strong></p></li>
  <li><p>Extending java.lang.Object. <strong>Sample 3</strong>. Works partially, Methods are being invoked. But</p>
  
  <ul>
  <li><em>How to plugin constructor with parameters?</em></li>
  <li><em>How to do the cast from this:[object Object] to other:jdk.nashorn.javaadapters.java.lang.Object@0, or vice versa?</em></li>
  </ul></li>
  <li><p>Implementing my custom class in Java and extend it in JavaScript. <strong>Sample 4</strong>. Works. But do I need Nashorn if I have to use Java?</p></li>
  </ol>
  
  <pre class="lang-js prettyprint-override"><code>var PriorityQueue = java.util.PriorityQueue;
  var HashMap = java.util.HashMap;
  var Integer = java.lang.Integer;
  
  // Sample 1
  // Doesn't work, equals and hashCode are not being invoked
  function Vertex1(from, cost) {
      this.from = from;
      this.cost = cost;
  
      this.equals = function(other) { return this.from == other.from; }
      this.hashCode = function() { return Integer.hashCode(this.from); }
  }
  
  var hm = new HashMap();
  hm.put(new Vertex1(1, 10), 10);
  hm.put(new Vertex1(1, 20), 21);
  // Prints size is 2, but I'd like to see 1
  print("HashMap size: " + hm.size());
  // Prints false
  print("HashMap1 contains: " + hm.containsKey(new Vertex1(1, 20)));
  
  // ------------------------------------------------------------------
  // Sample 2
  // Doesn't work, equals and hashCode are not being invoked
  function Vertex1(from, cost) {
      this.from = from;
      this.cost = cost;
  }
  Vertex1.prototype = {
      equals : function(other) { return this.from == other.from; },
      hashCode : function() { return Integer.hashCode(this.from); },
  }
  var hm = new HashMap();
  hm.put(new Vertex1(1, 10), 10);
  hm.put(new Vertex1(1, 20), 21);
  // Prints size is 2, but I'd like to see 1
  print("HashMap size: " + hm.size());
  // Prints false
  print("HashMap1 contains: " + hm.containsKey(new Vertex1(1, 20)));
  
  // ------------------------------------------------------------------
  // Sample 3
  // Works partially, Methods are being invoked. But 
  
  // 1. How to plugin construstor with parameters?
  // 2. How to do the cast from this:[object Object] to other:jdk.nashorn.javaadapters.java.lang.Object@0, or vice versa
  
  var JObject = Java.type("java.lang.Object");
  var Vertex2 = Java.extend(JObject, {
      from : 0,
      equals : function(other) { return this.from.equals(other.from); },
      hashCode : function() { return Integer.hashCode(this.from); },
  });
  var hm = new HashMap();
  // How to implement constructor for new Vertex2(10, 10)?
  hm.put(new Vertex2(), 10);
  hm.put(new Vertex2(), 21);
  // Prints size is 2, because hashCode is the same and equals returns false
  print("HashMap size: " + hm.size());
  // Prints false, because equals returns false
  print("HashMap1 contains: " + hm.containsKey(new Vertex2()));
  
  // ------------------------------------------------------------------
  // Sample 4
  // com.arsenyko.MyObject is implemented in Java, Works, but Nashorn is ambiguous then!!!
  var MyObject = Java.type("com.arsenyko.MyObject");
  var Vertex2 = Java.extend(MyObject, {});
  var hm = new HashMap();
  hm.put(new Vertex2(1, 10), 10);
  hm.put(new Vertex2(1, 20), 21);
  print("HashMap size: " + hm.size());
  print("HashMap1 contains: " + hm.containsKey(new Vertex2(1, 10)));
  </code></pre>
  
  <p><strong>EDIT 1</strong> </p>
  
  <p>@Tomasz, thanks. Have seen all the mentioned links. But though that somewhat undocumented exists. Almost gave up with Nashorn. Came to the following partial solution, methods are being invoked, constructor is being used, but <strong>how to cast <code>other.from</code> in the <code>equals</code> method in order to get access to the <code>from</code> field of the original object</strong> (this code produces different classes for each instance of a Vertex):</p>
  
  <pre class="lang-js prettyprint-override"><code>//load("nashorn:mozilla_compat.js");
  var PriorityQueue = java.util.PriorityQueue;
  var HashMap = java.util.HashMap;
  var Integer = java.lang.Integer;
  
  function Vertex1(from, cost) {
      this.from = from;
      this.cost = cost;
  
      this.equals = function(other) {
          var value1 = this.from;
          // How to get other.from here???
          var value2 = other.from;
          print('value1=' + value1 + ' value2=' + value2);
          print(other);
          var eq = value1.equals(value2);
          print('equals is ' + eq);
          return eq;
      }
      this.hashCode = function() {
          var hashCode = Integer.hashCode(this.from);
          print('hashCode is ' + hashCode);
          return hashCode;
      }
  
      var JObject = Java.type("java.lang.Object");
      // return Java.extend(JObject, this); // doesn't work
      // return this; // doesn't work
      // return new JavaAdapter(java.lang.Object, this); // Works! with load("nashorn:mozilla_compat.js");
      var Type = Java.extend.apply(Java, [JObject]);
      return new Type(this);
  }
  
  var hm = new HashMap();
  hm.put(new Vertex1(1, 10), 10);
  hm.put(new Vertex1(1, 20), 21);
  // Prints size is 2, but I'd like to see 1
  print("HashMap size: " + hm.size());
  // Prints false
  print("HashMap contains: " + hm.containsKey(new Vertex1(1, 20)));
  </code></pre>
  
  <p><strong>EDIT 2</strong></p>
  
  <p>Thanks for Tomasz, as he pointed out, Every invocation of the Java.extend() function with a class-specific implementation object produces a new Java adapter class. Thus we need to have one Object Extender and instantiate objects with that one type, as he showed in his sample. I modified it a little bit, so it produces instances with the same class with either factory or direct constructor, since we are using the same Object Extender</p>
  
  <pre class="lang-js prettyprint-override"><code>var HashMap = java.util.HashMap;
  var JInteger = java.lang.Integer;
  var JObject = Java.extend(java.lang.Object);
  
  var createVertex = (function() {
      var
      _equals = function(other) {
          print(this + ' vs ' + other);
          return this._from === other.from;
      };
      _hashCode = function() {
          var hashCode = JInteger.hashCode(this._from);
          print(hashCode);
          return hashCode;    
      };
      return function(from, cost) {
          return new JObject() {
              _from : from,
              _cost : cost,
              equals : _equals,
              hashCode : _hashCode,
          }
      }
  })();
  
  var JSVertex = function(from, cost) {
      return new JObject() {
          _from : from,
          _cost : cost,
          equals : function(other) {
              print(this + ' vs ' + other);
              return this._from === other._from;
          },
          hashCode : function() {
              var hashCode = JInteger.hashCode(this._from);
              print(hashCode);
              return hashCode;
          }
      }
  }
  
  var v1 = JSVertex(1, 10);
  var v2 = JSVertex(1, 20);
  //var v1 = createVertex(1, 10);
  //var v2 = createVertex(1, 20);
  var v3 = createVertex(1, 20);
  print(v1.class === v2.class); // returns true
  print(v2.class === v3.class); // returns true
  var hm = new HashMap();
  hm.put(v1, 10);
  hm.put(v2, 21);
  print("HashMap size: " + hm.size()); // Prints 2, but I'd like to see 1
  print("HashMap contains: " + hm.containsKey(v3)); // Prints false
  </code></pre>
  
  <p>However, there is a still an issue, the type of the parameter of the <code>equals</code> is <code>jdk.nashorn.javaadapters.java.lang.Object</code>, in other words the <code>other</code> and <code>this</code> inside <code>equals</code> are different types. <strong>Is there a way, to cast or get <code>_from</code> value from the object passed to the <code>equals</code></strong>?</p>
  
  <p><strong>THE SOLUTION</strong></p>
  
  <p><strong>See the solution for the problem is in the Tomasz's answer</strong>.</p>
  
  <p>Great work Tomasz! Thanks.</p>
  
  <p>PS : It's very sad that there is no neat and straightforward way to implement <code>equals</code> and <code>hashCode</code> in Nashorn. It would be useful for prototyping. Just compare that with this :)</p>
  
  <pre class="lang-groovy prettyprint-override"><code>import groovy.transform.EqualsAndHashCode
  @EqualsAndHashCode(excludes="cost")
  class Vertex {
     int from, cost
  }
  </code></pre>