Proper JavaScript OOP with inheritance for Java developers

Update: This post may still be correct but since ES6 the keyword “class” has been introduced to JavaScript, so make sure to check this out first: ES6 classes


The following is a personal best practice advice for OO with ES5 (and older)

Coming from a Java background, I really miss keywords like class or extends when I write JavaScript. Yes, I know. You can write OO-code in JavaScript but it’s really a mess using the prototype property. So I’ve been looking for a best practice for this for quite a while now. Reading through various tutorials I stumbled over some “hacks” (looked hackish to me at least) which never seemed to be really professional. Even the two JavaScript books I have here suggest two different ways of creating “class-like” constructs. However, after years of writing JavaScript now I think that I’ve found the best way to create class-like constructs including the idea of inheritance and overriding super methods which I would like to share with you.

I’ll demonstrate it with a simple…

Example

Let’s say we’d wanted to create a class “Vehicle” and a subclasses “Car”. The class “Vehicle” should provide a method “bringMeTo(Location location)” which “Car” should inherit and override..

In Java it’d probably look something like this (not tested):

public class Vehicle{

  int topSpeed;

  public Vehicle(int topSpeed){
    this.topSpeed = topSpeed;
  }

  public void bringMeTo(Location location){
    // ... do what ever you want to do here
  }
}

public class Car extends Vehicle{

  public String manufacturer;
  public String color;

  public Car(int topSpeed, String manufacturer, String color){
    super(topSpeed);
    this.manufacturer = manufacturer;
    this.color = color;
  }

  @Override
  public void bringMeTo(Location location){
    // Use an engine and 4 wheels to get to location  
  }
}

Now in JavaScript

Let’s see how we can transform into JavaScript, shall we? :-)

1. Preparation

First you’ll need a little helper function from CoffeeScript. It’s very handy since there is no extends keyboard in JavaScript, so add this to the very top of your JS code. Dont’ worry if you don’t understand the code, it’ll simply do all the funky JS OOP magic for you. All you need to know is that this basically does what Java’s extends keyword would do in a similar way.

var __hasProp = {}.hasOwnProperty;
var __extends = function(child, parent){
  for(var key in parent){
    if (__hasProp.call(parent, key)) child[key] = parent[key];
  }
  function ctor(){
    this.constructor = child;
  }
  ctor.prototype = parent.prototype;
  child.prototype = new ctor();
  child.__super__ = parent.prototype;
  return child;
};

2. Class definition, inheritance and method overriding

After adding our __extends script, we’ll write our two classes. You’ll be surprised how simple, clean and a little Java-like this’ll look:

// Class Vehicle
var Vehicle = (function() {

  // Constructor
  function Vehicle(topSpeed) {
    this.topSpeed = topSpeed;
  }

  Vehicle.prototype.bringMeTo = function(location) {
    // doSomething(location);
  };

  return Vehicle;

})();

// Class Car
var Car = (function(_super) {
  __extends(Car, _super); // Cool, eh? :-)

  // Constructor
  function Car(topSpeed, manufacturer, color) {

    // Setup public properties
    this.manufacturer = manufacturer;
    this.color = color;

    // Call super constructor
    return Car.__super__.constructor.apply(this, arguments);
  }

  // Override super method
  Car.prototype.bringMeTo = function(location) {

    // Call super method if you want
    Car.__super__.bringMeTo.call(this, location);
		
    // doSomethingElse();
  };

  return Car;

})(Vehicle);

There you go. Really simple.

Create instances by calling:

var v = new Vehicle(30);
var c = new Car(200, "Jaguar", "white");

Final notes

  • In Car, you can access the super class anytime by just using Car.__super__.
  • Override methods by adding a new function to Car.prototype
  • Don’t forget to set all public properties you need within the constructor (as shown above).

That’s it. Happy coding.

ZK user group conference in Mannheim, Germany

I’ll be speaking on the upcoming ZK user group conference in Mannheim, Germany on December 6th, 2011 (what’s ZK?).

If you enjoy developing rich internet applications with the power of Java, I highly recommend to be there.
If you want to join, just sign up for the event on zkoss.org for free and see three exciting presentations including ones from ZK head Timothy Clare and me.
Also, this is a great opportunity to meet other ZK developers and share some ideas and latest news in the community.

As a little teaser, here is the title sheet for my presentation.

.remove() and .contains() not working on your Java set?

If you suddenly experience problems callling set.remove() and set.contains() on your Java Set instances, you might be using Hibernate, which replaces your Set instance with its own version (PersistentSet), which uses a HashSet internally to store your data.

For example, the problem I faced was the following:

Person.java

public class Person(){

 String _name;

 public Person(String name){
  _name = name;
 }

 @Override
 public String toString(){
  return _name;
 }

 @Override
 public int hashCode(){
  if (_name != null) return _name.hashCode();
  return -1;
 }
}

MyTestApplication.java

public class MyTestApplication{

 public static void main(String[] args){

  Person steve = new Person("Steve");
  Set persons = new Set();
  persons.add(steve);
  System.out.println(persons);

  boolean contains = persons.contains(steve);
  System.out.println(String.valueOf(contains));

  persons.remove(steve);
  System.out.println(persons);

  for (Person person : persons){
   System.out.println("Contains: " + person.toString());
  }

 }
}

The code above prints the following to the console:

[Steve]
false
[Steve]
Contains: Steve

This definitely was not expected.

The reason: If you call set.remove(obj) or set.contains(obj), the internal HashSet will automatically call obj.hashCode() and use the returned value to search for obj.
Normally, this works without any issues, but there is one case where it doesn’t and that’s when:

  • You use Hibernate
  • You load the Set data eagerly
  • You override the obj.hashCode() method

The reason for this is a bug in Hibernate.

For me, a solution was to not override the obj.hashCode() method.
Since this is not a solution for all cases, I recommend to follow the link above and read through the comments. There are quite some workarounds described there. I’m sure one of them will work for you.

Good luck, soldier.

(God, Hibernate! I officially hate you now. It took me a whole day to find the reason for this issue).

Correct naming convention for boolean values in Hibernate mapping files

Ok, this just took me a while to figure out so I thought I’d share it here.

If you have a boolean variable that follows the naming convention “isValue” (e.g. boolean isPerson = false;), then the according mapping file property name may not include the “is” part of the name, for example:

<property name="person" column="is_person" />

Otherwise, Hibernate throws some weird Exceptions.
In my case it was:

Initial SessionFactory creation failed.org.hibernate.InstantiationException: could not instantiate test object FOO
Caused by org.hibernate.InstantiationException: could not instantiate test object BAR
Caused by: java.lang.reflect.InvocationTargetException
Caused by: java.lang.NoClassDefFoundError: Could not initialize class BAR

Hint: Inside your Java class, the following conventions must be used for getter and setters:

public boolean isPerson();
public void setPerson();

Don’t use:
public void setIsPerson();
!!!

New project: zkexamples.com

I created a new online platform called zkexamples.com.

The goal is to provide really simple and quick examples for common ZK use-cases and requirements (what is ZK?).

The idea is based on flexexamples.com which was a great help to me when I was coding a lot of Adobe Flex.

If anyone has some nice examples to share, feel free to sign up and just post it (requires review and approval by me).

I, personally, try to post every time I learned something new about ZK.
Might help someone else who has the same problem.