| ## Syntactic and semantic differences between Java and the generated Dart bindings |
| |
| This document highlights the key syntactic and semantic variations between Java |
| and Dart and how JNIgen addresses them to ensure smooth interoperability. |
| |
| ### Method overloading |
| |
| Java supports overloading methods. This means that it can distinguish between |
| two methods with the same name that have a different signature. |
| |
| ```java |
| // Java |
| public class Calculator { |
| public int add(int a, int b) { |
| return a + b; |
| } |
| |
| public double add(double a, double b) { |
| return a + b; |
| } |
| |
| public float add(float a, float b) { |
| return a + b; |
| } |
| } |
| ``` |
| |
| This is not the case for Dart. Each method of a class must have a unique name. |
| To overcome this limitation, JNIgen adds a dollar sign (`$`) and a numeric |
| suffix to the end of the overloaded method name. |
| |
| ```dart |
| // Dart Bindings - Boilerplate omitted for clarity. |
| class Calculator extends JObject { |
| int add(int a, int b) { /* ... */ } |
| |
| double add$1(double a, double b) { /* ... */ } |
| |
| double add$2(double a, double b) { /* ... */ } |
| } |
| ``` |
| |
| > [!WARNING] |
| > Running the code generator again on a different version of the library can map |
| > the methods differently if the order of the methods change or another |
| > overloading of the same method gets added. |
| > |
| > Double check the doc comments on the generated methods to make sure you are |
| > calling your intended method. |
| |
| You might wonder why the method isn't renamed to `add1` instead of `add$1`. The |
| reason is that a method named `add1` could already exist in the Java class. On |
| the other hand, Java identifiers normally do not contain dollar signs. |
| |
| > [!NOTE] |
| > See |
| > [Identifiers containing dollar signs](#identifiers-containing-dollar-signs) to |
| > see what JNIgen does when identifiers contain dollar signs. |
| |
| ```java |
| // Java |
| public class Calculator { |
| public int add(int a, int b) { |
| return a + b; |
| } |
| |
| public double add(double a, double b) { |
| return a + b; |
| } |
| |
| public float add(float a, float b) { |
| return a + b; |
| } |
| |
| public int add1(int a) { |
| return a + 1; |
| } |
| |
| public double add1(double a) { |
| return a + 1; |
| } |
| } |
| ``` |
| |
| In this case, the generated code will be: |
| |
| ```dart |
| // Dart Bindings - Boilerplate omitted for clarity. |
| class Calculator extends JObject { |
| int add(int a, int b) { /* ... */ } |
| |
| double add$1(double a, double b) { /* ... */ } |
| |
| double add$2(double a, double b) { /* ... */ } |
| |
| int add1(int a) { /* ... */ } |
| |
| double add1$1(double a) { /* ... */ } |
| } |
| ``` |
| |
| ### Fields and methods with the same name |
| |
| In Java, we can have a field and a method with the same name. This is not |
| possible in Dart. |
| |
| ```java |
| // Java |
| public class Player { |
| // Player's personal duck! |
| public Duck duck; |
| |
| // Lower your head! |
| public void duck() { /* ... */ } |
| } |
| ``` |
| |
| JNIgen handles this similarly to [method overloading](#method-overloading). The |
| method with the same name as the field will be appended by a dollar sign (`$`) |
| followed by a numeric suffix. |
| |
| ```dart |
| // Dart Bindings - Boilerplate omitted for clarity. |
| class Player extends JObject { |
| Duck get duck { /* ... */ }; |
| set duck(Duck value) { /* ... */ } |
| |
| void duck$1() { /* ... */ } |
| |
| // If there were more `duck` methods they would be named |
| // `duck$2`, `duck$3`, ... |
| } |
| ``` |
| |
| It is important to note that the order of renaming is fields-first |
| methods-second. So when both a field named `duck` and a method named `duck` |
| exist, the field keeps its original name as seen above. |
| |
| However the renaming happens for superclasses first. Consider this case. |
| |
| ```java |
| // Java |
| public class Player { |
| // Lower your head! |
| public void duck() { /* ... */ } |
| } |
| |
| public class DuckOwningPlayer extends Player { |
| // Player's personal duck! |
| public Duck duck; |
| } |
| ``` |
| |
| The `Player` class already has a method named `duck` and no field with the same |
| name. So this will be the generated bindings for it: |
| |
| ```dart |
| // Dart Bindings - Boilerplate omitted for clarity. |
| class Player extends JObject { |
| void duck() { /* ... */ } |
| } |
| ``` |
| |
| `DuckOwningPlayer` inherits the `duck()` method from `Player` and adds a field |
| named `duck`. This time, the field will be renamed as the method is simply |
| inherited. |
| |
| ```dart |
| // Dart Bindings - Boilerplate omitted for clarity. |
| class DuckOwningPlayer extends Player { |
| Duck get duck$1 { /* ... */ }; |
| set duck$1(Duck value) { /* ... */ } |
| } |
| ``` |
| |
| ### Identifiers containing dollar signs |
| |
| JNIgen uses dollar signs in the generated code to fill the syntactic and |
| semantic gaps between Java and Dart. This normally does not cause a problem as |
| the |
| [Java language specificifaction](https://docs.oracle.com/javase/specs/jls/se11/html/jls-3.html#jls-3.8) |
| suggests dollar signs should be used only in the generated code or, rarely, to |
| access pre-existing names on legacy systems. |
| |
| JNIgen replaces each single dollar sign with two dollar signs. For example |
| `generated$method$2` will turn into `generated$$method$$2`. This prevents name |
| collision as JNIgen-renamed identifiers will end with an odd number of dollar |
| signs (optionally followed by a numeric suffix). |
| |
| ### Identifier starting with underscore (`_`) |
| |
| Unlike Java, Dart identifiers that start with an underscore are private. This |
| means they are inaccessible outside their defining scope. Users should still be |
| able to access public Java identifiers that start with an underscore from the |
| generated Dart bindings. To allow this, JNIgen prepends such identifiers with a |
| dollar sign to keep them public in Dart. |
| |
| For example, `_Example` in Java will be converted to `$_Example` in the Dart |
| bindings: |
| |
| ```java |
| // Java |
| public class _Example { |
| public void _printMessage() { |
| System.out.println("Hello from Java!"); |
| } |
| } |
| ``` |
| |
| ```dart |
| // Dart Bindings - Boilerplate omitted for clarity. |
| class $_Example extends JObject { |
| void $_printMessage() { /* ... */ } |
| } |
| ``` |
| |
| ### Inner classes |
| |
| Java has the concept of inner classes, while Dart does not. Therefore, inner |
| classes are generated as separate classes named using the name of their |
| outer-class followed by a dollar sign (`$`) followed by their original name. |
| |
| For example: |
| |
| ```java |
| // Java |
| public class Outer { |
| public class Inner {} |
| } |
| ``` |
| |
| will be turned into: |
| |
| ```dart |
| // Dart Bindings - Boilerplate omitted for clarity. |
| class Outer extends JObject {} |
| |
| class Outer$Inner extends JObject {} |
| ``` |