11 Major Differences & Similarities Between Java Classes & ECMAScript 2022 JavaScript Classes For Java Coders

Introduction

On the last years java and javascript have become more closer in their structural syntax. By saying that I of course don’t mean that one can run java from the browser or that javascript became strongly typed and compilable. I mean it in the sense that these two programming languages both introduce similar principles of object oriented programming. This similarity enables Java programmers to make the shift to javascript when needed while maintaining the same coding principles they are used to as much as possible. Yet, since there is a basic difference between the way both programming languages were built a java pro who is a newbie to javascript needs to know how things are implemented in javascript so that basic mistakes won’t be part of the code, whether it runs on a browser or accesses a nosql database such as MongoDB. For the sake of focusing on what matters I’ll examine 11 major differences & similarities that can be found and will discuss each of them briefly.

1. Almost Strong Typing in Javascript? If You Want It, You Got It.

In Java it’s basic to have an object strongly typed either by a primitive type or an object. Allocation has to be with an explicit object name:

Class Person {
    private String firstName;
    private String lastName;
    
    Person(String firstName, String lastName) {
       this.firstName = firstName;
       this.lastName = lastName;
    }

    private String fullName(String prefix) {
       if (prefix != null)
           return prefix + " " + this.firstName + " " + this.lastName;
    }
    
    public String getFirstName() {
      return this.firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
      return this.lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
}

Person aPerson = new Person("JavaJohn", "JavaDoe");

But in javascript one can just allocate an anonymous object and that’s it:

const Person = { firstName: "John", lastName : "Doe" }

Since ES6 the closer version of an object allocation in Java may be:

class Person {
   constructor(firstName, lastName) {
       this.firstName = firstName;
       this.lastName = lastName;
   }
}

const aPerson = new Person('Jonathan', 'Does');

As you can see the addtion of a constructor keyword to describe a contructor alongside with initialization parameters and the this keyword (that’s similar to the one used in java) makes the definition of a simple class easily understood to a java coder.

In a addition, the allocation of an object uses, just like in the Java code the keyword new accopanied with the object name and its initialization parameters.

2. Properties Assignment is Essentially Their Declaration

Java is strongly typed as mentioned. Hence, as you know, properties such as the firstName and the lastName must be declared somewhere within the class definition, otherwise your code won’t compile. Javascript is much more flexible when it comes to that. In javascript the first time you assign a value to a variable or a constant is effectively the time you allocate it for the for first time. Furthermore – on each and every assignment the type of the property can change. So you can assign a value to a property in 3 ways:

  1. during the object initialization
constructor(firstName, lastName) {
       this.firstName = firstName;
       this.lastName = lastName;
}

2. Using an assignment outside of the class definition:

aPerson.middleName = "Walter";

3. By treating a property as another field in a javascript array, again, outside the class definition:

aPerson["middleName"] = "Walter";

3. Methods Definitions Follow The Definition Principles of Java

Javascript had always included functions as part of the language. They were similar to good old C and C++ functions. Here’s an example for such a function that adds two numbers:

function add(a,b) {
   return a+b;
}

It looks like the creators of Javascript, understood that the good old OOP mechanism C++ and Java have to define a method was simpler, and so they decided to neglect the function keyword and replace it with a simple method name followed by a list of parameters.

class Person {
   constructor(firstName, lastName) {
       this.firstName = firstName;
       this.lastName = lastName;
   }
  
   fullName(prefix) {
                 if (prefix != null)
                     return prefix + " "+this.firstName + " " + this.lastName;
                 return this.firstName + " " + this.lastName;
  }
  
};

const aPerson = new Person("John","Doe");
console.log(aPerson.fullName("Mr."));

Pay attention that a javascript method doesn’t return a specific type the way Java demands and that no type checking is forced on a method’s parameter. That’s up to the coder to do that if needed.

4. Getters Definitions are Abbreviated vs. Their Java Counterparts

In Java, using getters to get a property value is basic. You have to define a property object and special method that starts with the word get followed by the name of the property. Yet, in javascript a more shorter form is being used and its usage is less basic that Java has.

const globalMaxAge = 120;

class Person {
   constructor(firstName, lastName,age) {
       this.firstName = firstName;
       this.lastName = lastName;
       this.age = age
   }
   
   // the definition of a getter that's attached to a property
   get paddedFirstName() {
      return "    "+this.firstName+"   ";
   } 

   // The definition of a getter that relies on all available properties & external data
   get testedAge() {
      if (this.age > globalMaxAge)
          return -1;
      else
          return this.age;
   }
  
   fullName(prefix) {
                 if (prefix != null)
                     return prefix + " "+this.firstName + " " + this.lastName;
                 return this.firstName + " " + this.lastName;
  }
  
};

const aPerson = new Person("John","Doe", 27);
console.log(aPerson.firstName);
console.log(aPerson.paddedFirstName);
console.log(aPerson.testedAge);

As you can see two get methods were defined: the paddedFirstName and the testedAge. Their usage is to have a kind of a computation over some properties and other available data and return a result. paddedFirstName adds spaces before and after the firstName property. testedAge makes sure a legit age will be returned. It’s important to note though, that you cannot use a get method name whose exactly the same one of the assigned properties. In this case you’ll get an error. You can however add a getter with a name such as getFirstName. However, I wouldn’t recommend you doing that since it will be confusing to read the javascript code when you will access the getter. The reason is that in javascript, in cases when you access a method without stating its parameters the result will be that you’ll get the method’s definition, and not the result of its computation. But in the case you’ll try to access a getXXXX method you’ll get as a result a real property value. That’s not professional.

5. Setters Are Effectively Implicit Methods That Hide a Real Property

While Java allows (but do not force) setter methods to securely set a property by using a naming convention of setXXXXX where XXXXX is the property name, javascript, in my point of view, handles setters as a tool harden the visibility of the actual property name.

Thus, while in java definition of setter that sets the family name may look like:

private void setLastName(String lastName) {
   this.lastName = lastName;
}

In javascript it may look like this:

set lastNameSetter(lastName) {
    this.lastName = lastName;
}

You may ask why didn’t I use the setter function name the same as the one I used in java. The reason for doing that relies on the way setters are being called in javascript. While in java calling a setter may be something like:

aPerson.setLastName("Davidson");

Javascript handles this calling in an entirely different way:

aPerson.lastNameSetter = "Davidson";

This means that there isn’t a new real property named as “lastNameSetter” but a new property whose name is lastName. In fact, you can use any kind of name for the set method, as long as it isn’t exactly the actual property name (in our case it’s “lastName”).

(By The Way: For those of you who are old enough to remember, the same way of calling was once used in the VCL object paradigm within good old C++Builder).

6. Javascript Creators Had Discovered There is a Need For Class Fields To Be Private And Implemented It. Wow!

What you read is what you’ll get. Private fields are here to stay and all you have to do in order to define a field as private is to prefix it with a hashtag (#). For instance, if our person object should contain a private id of a person it may look like this:

class Person {
    #userId;
    constructor(id) {
       this.#userId = id;
    }
.
.
}

For some reason the maintainers of javascript decided to stick with the principle (that java implements) that a private field should be declared before its usage. I don’t know why all of the sudden it became so important to them, but that’s how it works. In addition private fields are just like in java – they are private to the class – and so they are not accessible from a class’s instance. Nonetheless, just like in java, you can use them in the class’s methods including getters and setters.

Although it may look natural to do so a private field cannot be initialized inside a class without using a method (either a constructor or a non-constructor). This is totally different from java were you can initialize a private field right at the class instantiation:

class Person {
    private int userId = 5000;
    public Person(int userId) {
         this.userId = userId;
    }
}

7. What About Methods Modifiers? Does Javascript Implement Public, Protected and Private Methods?

Well, not exactly. Here how it goes. Basically whenever you define a class’s method without any other additions it’s considered public. It’s just the opposite from Java which defines such methods as private.

As for private methods – these are implemented in the same manner private fields are defined – with a hashtag at the beginning of the method name:

class Person {
   #taxesPaid;
   constructor() {
     this.#taxesPaid = 0;
   }
   #addToTaxesPaid(amount) {
       this.#taxesPaid += amount;
   }
   handleTaxes() {
      this.#addToTaxesPaid(2000);
   }
}

const aPerson = new Person();
aPerson.handleTaxes();

Pay attention that although #addToTaxesPaid is private you can still indirectly activate it using the mediating public method handleTaxes.

In addition getters and setters can be private by just adding a hashtag at the begining of the method.

And oh yeah, as for protected methods – there isn’t a support for that. Javascript maintainers didn’t probably think its that important. Javascript, go figure.

8. One Crazy Javascript Thing That Will NEVER Occur On Java Code

Javascript is very flexible unlike java. Sometimes it can be too flexible up to a level you won’t know what kind of object you are handling because the object “lost” parts of it. This may occur after the delete operator is used on one of the fields.

The delete operator is used for the removal of fields out of an object in a way that accesing them after this operator had been used would yield an “undefined” state for that field. From what I learned, it’s espcially useful when you want to transform an object from one format to another without messing with each an every field (and that may happen alot). However you must be aware that whenever you are trying to access a field/property and get “undefined” for its state, then maybe something happened along the way that made the field to be deleted, otherwise all of those reddish error notification rows in the console may appear.

9. OOP Juicy Stuff #1: Extending an Object

In java, extending an object is a no brainer. You just add the extends keyword followed by the extended class name. By doing so, you benefit of all of the class’s properties and methods and can add and override them with your own properties and methods.

class Person {
   private String firstName;
   private String lastName;

   public Person() {
      this.firstName = "John";
      this.lastName = "Doe";
   }

   public String getFirstName() {
      return this.firstName;
   }

   public String getLastName() {
      return this.lastName;
   }

   public printFullName() {
     System.out.println(this.firstName + " " + this.lastName);
   }
}

class HappyPerson extends Person {
   private int happinessLevel;
   public HappyPerson(int happinessLevel) {
      happinessLevel = happinessLevel;
   }
   public int getHappinessLevel() {
     return this.happinessLevel;
   }
}

//making a call in an external method:
HappyPerson aHappyPerson = new HappyPerson(9)
System.out.println(aHappyPerson.getHappinessLevel);
aHappyPerson.printFullName();

In this code block you can see that aHappyPerson gains access to printFullName which exists within its direct ancestor class, Person, while still having the capability to indirectly access Person’s properties, firstName & lastName, by calling their getter methods. Of course, I could have defined firstName & lastName to public, and let them be inherited to aHappyPerson, but that is a less common practice for properties definitions in Java.

Javascript in ECMAScript 6 tries achieve this behaviour of inheritence almost the same as java does, with one small difference. The difference is that the extending object must call the super() method or super with parameters (if those are required) within the its constructor method. Otherwise, the ancestor’s properties won’t be available to the extending class and an error will occur. This only applies to public properties since private properties cannot be inherited.

class Person {
   constructor() {
    this.firstName = "John";
    this.lastName = "Doe";
   }
   printFullName() {
      console.log(this.firstName + ' '+ this.lastName);
   }
};

class HappyPerson extends Person {
    constructor(happinessLevel) {
       super();
       this.happinessLevel = happinessLevel;
    }
}

const aHappyPerson = new HappyPerson(9);
console.log(aHappyPerson.firstName + ' happiness level is ' + aHappyPerson.happinessLevel);
aHappyPerson.printFullName();

There is also a possibility to extend an anonymous class, but I don’t want to elaborate on this one because it’s beyond the scope of this article.

10. Overloading in Java Becomes Offloading in Javascript

Overloading is one of the features java programmers can use in order to handle nuances of a similar operation. For instance if a coder wants to print a person’s full name, it can be done by printing the first and last name of a person, or prefixing it with “Mr.”, “Mrs.” or otherwise. Hence, overloading can become handy and allows the definitions of the same method name with different parameters list (in this case one that accepts a prefix parameter and another one that doesn’t).

class Person {
   private String firstName;
   private String lastName;

   public Person() {
     this.firstname = "John";
     this.lastName = "Doe";
   }

   public void printFullName() {
      System.out.println(firstName + " " + lastName);
   }

   public void printFullName(String prefix) {
      System.out.println(prefix + " " +firstName + " " + lastName);
   }
}

But here’s the point where javascript’s shortcoming arises: it doesn’t have an organized and intrinsic solution for the methods overloading. This means that defining such a class with both methods ends up with a situation where only the last defined method will be used when called (even if there is a difference in the parameters each method has).

class Person {
   #firstName;
   #lastName;

   constructor() {
     this.#firstName = "John";
     this.#lastName = "Doe";
   }

   printFullName() {
      console.log(this.#firstName + " " + this.#lastName);
   }

   printFullName(prefix) {
      console.log(prefix + " " +this.#firstName + " " + this.#lastName);
   }
}

const aPerson = new Person();
console.log(aPerson.printFullName());
// Output:
// "undefined John Doe"
// and immediately after that: "undefined"

As you can see this kind of behaviour of javascript makes the predication of the result quite hard, since it ran the second defined method of printFullName, but without a required parameter which led to a print of “undefined” alongside with first and last names. It also printed undefined, since the second method definition hid the first one, but the code called the implementation of the first method.

So, as you can see, overloading is not really implemented in javascript and thus everything get messy. I would not recommend you to rely on this behaviour in order to mimic overloading even in cases where you are passing the correct paramters because things may change later an arise more errors.

11. Overriding a Method (and Accessing Its Containing Class Properties)

I don’t know what did javascript maintainers thought to themselves, but the overriding of a method in javascript is a sort of a half way to what real OOP languages such as java can offer.

Basically you can override a method including its parameters. Sounds great, no? Well, not exactly, since you first have to call the super() function in the constructor in order to get access to the public properties of the derived class.

What about protected properties? Javascript doesn’t support those. So I guess they solved this “problem” by just making it irrelevant in the first place….

Here’s an example that summarizes how to implement methods (and public properties) overriding:

class Person {  
   constructor() {
     this.firstName = "John";
     this.lastName = "Doe";
   }
  
   printFullName(prefix) {
      console.log(prefix + " " +this.firstName + " " + this.lastName);
   }
}

const aPerson = new Person();
console.log("aPerson output:");
aPerson.printFullName("Mr.");

class HappyPerson extends Person{
   #happinessLevel;
   constructor (happinessLevel) {
     super();
     this.#happinessLevel = happinessLevel;
   }
   printFullName(prefix) {
       console.log(prefix + " " + this.firstName + " " + this.lastName + " happiness level is " + this.#happinessLevel);
   }
   getFirstName() {
     return this.firstName;
   }
}
const aHappyPerson = new HappyPerson(8);
console.log("aHappyPerson output:");
aHappyPerson.printFullName("Mr.");

//Output:
// "aPerson output:"
// "Mr. John Doe"
// "aHappyPerson output:"
// "Mr. John Doe happiness level is 8"

As you can see both instances addressed the correct class’s method, yet HappyPerson couldn’t have accessed firstName and lastName properties of Person without first calling super() in its constructor, so bear that in mind when inheriting classes.

12. Surprise Surprise! Javascript Polymorphism is On! Well, Almost

Just when I was about to give up on javascript as a read OOP language I was surprised that polymorphism is implemented in it to almost to its fullest. That means that you can “blindly” call the same method for sibling classes, like in this example:

class GeometricObject { 
  area() {
    console.log("Computing the area of a geometric object....");
  }
}

class Rectangle extends GeometricObject {
  constructor(width,height) {
    super();
    this.width = width;
    this.height = height;
  }
  area () {
    return this.width*this.height;
  }
}

class Triangle extends GeometricObject {
   constructor(base,height) {
     super();
     this.base = base;
     this.height = height;
   }
   area() {
     return this.base * this.height / 2;
   }
}

var aGeometricObject = new Rectangle(5,8); 
console.log(aGeometricObject.area()); // yields 40
aGeometricObject = new Triangle(5,8);
console.log(aGeometricObject.area()); // yields 20

Like in previous examples that involve inheritence, you must call the super() function in the constructor of each child class in order to gain access to its parent’s methods (in this case – it’s the area method).

Unlike in java where there’s a posibility to use a vitrtual method within the ancestor class which is not runnable, javascript allows the running of the ancestor method regardless of its having child classes with identical names or not. Thus making the concept of a virtual method as irrelevant.

Furthermore, the fact that javascript is a weakly typed or untyped language, removes the most basic caveat of assigning any kind of class instance into a variable that was already assigned with a specific class, hence eliminating the coercion of a method to be part of a class which is relates to its ancestor’s tree.

Conclusion

Javascript is yet to reach to the level of OOP programming Java offers. Yet, great steps have been done in the last few years so that javascript will handle most of the principles which in are use in OOP languages. I guess that if you want to do javascript coding the move from java will be more pleasent than what it was, Nonetheless, since javascript isn’t a compilable, strongly typed language, you’ll still have to bear in mind that many of bugs, including those that are related to OOP, are easily dealt with at compile time in java, but in javascript they will pop up at runtime.

Using this article you may be able to program in a most “java-ish” basic manner, however, whenever you’ll need more flexibility in dynamically adding code to a class instance, or performing operation which are similar to java reflection, further articles who explain these will be published in the coming future.

Leave a Reply

Your email address will not be published.