14 novembro 2008

Java/JavaFX integration: Implementing a Java Interface on JavaFX and Multiple Inheritance

One of most curious things of JavaFX is the possibility of multiple inheritance, a feature not present on it's "parent language" Java (take a look here). When the Java was created, the designers decided that multiple inheritance was a confusing and not optimized thing. So, to avoid future problems, they created Interfaces, an virtual collection of method signatures that must be implemented by an concrete class, but there're no self implementation.

Resuming, an Interface can extends various other Interfaces, and a Class can implement various Interfaces, but it can extend only one other class. When a Class implements an Interface, we say that it implements those methods specified on that Interface.

Back to JavaFX: JavaFX hasn't Interfaces, and permits multiple inheritance of classes. This is a valid code:
public class ClassA extends ClassB, ClassC {

}

And that classes can be JavaFX classes or Java classes..

But, if JavaFX doesn't have Interface, how can we implement a Java interface? It's simple: JavaFX consider Java interfaces as abstract classes with abstract methods. So, it's possible to do things like this:
public class MyJavaFXClass extends java.io.Serializable, java.io.InputStream, java.io.StringWriter {
...
}

Serializable and InputStream are java interfaces, and StringWriter is a concrete class.

It works very well, but there are some things that we need to note: some of Java features was not implemented in JavaFX yet, like varargs, enums and generics. Lets look each one:
  • Generics: is not a problem to inheritance. You can simple ignore then when implement a method. Java don't distinguishes between Collection and Collection in a method signature.

  • Enum: can be used on method signatures, but it's manipulated as a common object. You can't access enum values direct, like MyEnum.VALUE1. But you can use myEnumValue.name() method to compare string name of the values. Or you can user MyEnum.values() static method to get all values declared there.

  • Primitive types: It isn't a problem. JavaFX doesn't have primitive types, but if you have a method that receive a primitive type, you can use the relative object. Look for correspondences here.

  • VarArgs: it's the more problematic feature to JavaFX. You can't implement an interface on a concreate class that has methods with varargs! VarArgs aren't arrays! And there are no other form to substitute then. So, to resolve this problem, you can implement an abstract Java class that implements that methods and delegate then to other method, implemented by your JavaFX class. It's your unique solution now. But I think that VarArgs will be implemented in a near future in JavaFX, because it exists in a current-build's reflection class javafx.reflect.FXFunctionType (on 11/14/2008).

  • Arrays attribute: this is a very important problem. If I have a Java interface/class with an method that receipt an array of elements, I can't override that. Example:
Java interface:
public interface TesteI1 {

public void method1(String[] values);

}

JavaFX class:
public class TesteF1 extends TesteI1 {
public function method1(values: String[]):Void {

}
}
This is a wrong code. Why? Because String[] in JavaFX isn't an array, but is a Sequence! To show this, use this code:
var f = ["String1", "String2"];
java.lang.System.out.println("f: {f.getClass()}");

This will show you that f is a com.sun.javafx.runtime.sequence.ArraySequence. You can send it as a parameter to a method that receives an array, like java.util.Arrays.asList(f), but it isn't an array! I think that it's will be corrected in the future.

Nenhum comentário: