We originally started with the idea that when we encounter a non-JsType that is a Single-Abstract-Method class (SAM), we will automatically threat it as a javascript function for interoperability purposes.
For that to work well, we need to make it work in a predictable way. Which means;
1. Behavior shouldn't be broken with runtime type information.
i.e. if the method accepts interface 'Consumer' it should accept any implementation of Consumer, not some of them (e.g. object that implements both Consumer and Runnable).
2. Referential equality should be preserved at least per SAM definition.
i.e. element.addEventListener(e) and element.removeEventListener(e) should result in same function given that both methods uses the same SAM interface.
I don't think we can diverge from these two in our final solution. However, this forces us to generate bridge methods that is more complex to implement, results in extra code bloat and also it can be surprising (e.g. an object that happens to be a SAM but meant to be an opaque object).
Anyway, after long discussions we converged into an alternative.
We are planning to introduce javascript functions to our type system and explicitly mark them by @JsFunction that has its own restrictions similar to JsTypes. We will forbid concrete classes to implement/extend multiple @JsFunction types. With this, they can handled efficiently and will be more explicit and will behave predictable.
This option basically prevents simple consumption of java functional interfaces that appear on JsTypes but we can later handle this (if necessary) via @JsConvert. This will make 'function <=> SAM' conversion more explicit. As an additional benefit, it will follow the same code path as the rest of the @JsConvert while generating the bridge methods. E.g.
@JsConvert(SamToFunctionConverter.class)
public @Interface ToJsFunction {}
void forEach(@ToJsFunction Consumer c);
I'll update the doc soon. Let us know what you think.
Cheers.