Open utybo opened 7 years ago
require(
1 == java.util.Collections.min(
list[5, 1, 2, 3],
asInterfaceInstance(
java.util.Comparator.class,
|a, b| -> Integer.compare(a, b))),
"err")
works as expected...
Huh, I forgot about that method... I expected it to work by just putting a closure but it seems Comparator doesn't count as a functional interface. Adapters still yield an error though I wonder if it's relevant since there is this asInterfaceInstance function
Sorry for my ignorance >_<
It should be automatically converted, but we have a bug in identifying SAM since 1.8, since now interfaces can have default methods. The asInterfaceInstance
is a way to force the conversion explicitly.
Although in this particular case, the adapter is not necessary, I think the found behavior should be investigated, since it can be the sign of an underlying bug.
Moreover, Comparator
is a functional interface, so it should work. Indeed,
require(
1 == java.util.Collections.min(
list[5, 1, 2, 3],
asFunctionalInterface(
java.util.Comparator.class,
|a, b| -> Integer.compare(a, b))),
"err")
fails (since it only creates an adapter under the hood), as well as
require(
1 == java.util.Collections.min(
list[5, 1, 2, 3],
|a, b| -> Integer.compare(a, b)),
"err")
failing to wrap the closure in a FI using a LambdaMetafactory
I actually have 2 old branches try to fix this aspect (https://github.com/yloiseau/golo-lang/tree/improvement/java-function-integration and https://github.com/yloiseau/golo-lang/tree/wip/java-function-integration) if someone wants to have a look :smile:
Here are some minimal tests:
@FunctionalInterface
public interface FI {
int get();
}
@FunctionalInterface
public interface FiWithDefault {
int get();
default int apply() {
return get() + 1;
}
}
@FunctionalInterface
public interface FiWithEquals {
int get();
boolean equals(Object other);
}
public interface SAM {
int get();
}
public interface SamWithDefault {
int get();
default int apply() {
return get() + 1;
}
}
public interface SamWithEquals {
int get();
boolean equals(Object other);
}
public final class Utils {
public static int useSam(SAM f) {
return f.get();
}
public static int useFi(FI f) {
return f.get();
}
public static int useSamWithDefault(SamWithDefault f) {
return f.get();
}
public static int useFiWithDefault(FiWithDefault f) {
return f.get();
}
public static int useSamWithDefaultUseDefault(SamWithDefault f) {
return f.apply();
}
public static int useFiWithDefaultUseDefault(FiWithDefault f) {
return f.apply();
}
public static int useSamWithEquals(SamWithEquals f) {
return f.get();
}
public static int useFiWithEquals(FiWithEquals f) {
return f.get();
}
}
test.golo
:
module Test
import gololang.AnsiCodes
import gololang.Adapters
function title = |t| {
println("")
bold()
fg_magenta()
println(t)
reset()
}
function test = |d, e, f| {
print("▶ ")
fg_blue()
print(d)
reset()
print(": [")
try {
let r = f()
require(r == e, "expected %s, got %s": format(e, r))
fg_green()
print("OK")
reset()
println("]")
} catch (e) {
if (e oftype AssertionError.class) {
fg_yellow()
print("FAILED")
reset()
println("]")
fg_cyan()
println(" " + e: message())
reset()
} else {
fg_red()
print("ERROR")
reset()
println("]")
fg_cyan()
println(" " + e)
reset()
}
}
}
function main = |args| {
title("Pure SAM and FI")
test("sam with closure", 42,
-> Utils.useSam(-> 42))
test("fi with closure", 42,
-> Utils.useFi(-> 42))
test("sam asInterfaceInstance", 42,
-> Utils.useSam(asInterfaceInstance(SAM.class, -> 42)))
test("fi asFunctionalInterface", 42,
-> Utils.useFi(asFunctionalInterface(FI.class, -> 42)))
title("SAM and FI with default method (use abstract)")
# fails since SamWithDefault is not identified as a SAM due to the default
# method (see o.e.g.runtime.TypeMatching.isSam)
test("sam with closure", 42,
-> Utils.useSamWithDefault(-> 42))
test("fi with closure", 42,
-> Utils.useFiWithDefault(-> 42))
test("sam asInterfaceInstance", 42,
-> Utils.useSamWithDefault(asInterfaceInstance(SamWithDefault.class, -> 42)))
test("fi asFunctionalInterface", 42,
-> Utils.useFiWithDefault(asFunctionalInterface(FiWithDefault.class, -> 42)))
title("SAM and FI with default method (use default)")
# fails since SamWithDefault is not identified as a SAM due to the default
# method (see o.e.g.runtime.TypeMatching.isSam)
test("sam with closure", 42,
-> Utils.useSamWithDefaultUseDefault(-> 41))
test("fi with closure", 42,
-> Utils.useFiWithDefaultUseDefault(-> 41))
# fails: the MethodHandleProxies does not define nor use the default interface method.
test("sam asInterfaceInstance", 42,
-> Utils.useSamWithDefaultUseDefault(asInterfaceInstance(SamWithDefault.class, -> 41)))
# fails: the adapter does not define nor use the default interface method.
test("fi asFunctionalInterface", 42,
-> Utils.useFiWithDefaultUseDefault(asFunctionalInterface(FiWithDefault.class, -> 41)))
test("sam with adapter (manual delegate)", 42,
-> Utils.useSamWithDefault(Adapter(): interfaces(["SamWithDefault"])
: implements("get", |this| -> 42)
: implements("apply", |this| -> ^SamWithDefault::apply(this))
: newInstance()))
test("fi with adapter (manual delegate)", 42,
-> Utils.useFiWithDefault(Adapter(): interfaces(["FiWithDefault"])
: implements("get", |this| -> 42)
: implements("apply", |this| -> ^FiWithDefault::apply(this))
: newInstance()))
title("SAM and FI with equals")
# fails since SamWithEquals is not identified as a SAM due to the abstract
# equals method, even if inherited from Object (see o.e.g.runtime.TypeMatching.isSam)
test("sam with closure", 42,
-> Utils.useSamWithEquals(-> 42))
test("fi with closure", 42,
-> Utils.useFiWithEquals(-> 42))
test("sam asInterfaceInstance", 42,
-> Utils.useSamWithEquals(asInterfaceInstance(SamWithEquals.class, -> 42)))
# fails: the adapter does not define nor use the inherited equals method.
test("fi asFunctionalInterface", 42,
-> Utils.useFiWithEquals(asFunctionalInterface(FiWithEquals.class, -> 42)))
# fails: the adapter does not accept the equals overriding. Why ?
# probably a conflict with the one inherited from Object
test("sam with adapter (override)", 42,
-> Utils.useSamWithEquals(Adapter(): interfaces(["SamWithEquals"])
: implements("get", |this| -> 42)
: overrides("equals", |super, this, other| -> this == other)
: newInstance()))
# fails: the adapter does not accept the equals implementation. Why ?
# probably a conflict with the one inherited from Object
test("sam with adapter (implements)", 42,
-> Utils.useSamWithEquals(Adapter(): interfaces(["SamWithEquals"])
: implements("get", |this| -> 42)
: implements("equals", |this, other| -> this == other)
: newInstance()))
}
Makefile
:
CLASSES := $(addsuffix .class, $(basename $(wildcard *.java)))
.PHONY: test
test: $(CLASSES)
@golo golo --classpath . --files test.golo
%.class: %.java
@javac -d . $<
clean:
@rm -f *.class
The Java interface java.util.Comparator has, for some reason, the equals method as an abstract method. Even though it is implemented by java.lang.Object for every object, this seems to confuse Golo which spits out a
There is no implementation or override for: public abstract boolean java.util.Comparator.equals(java.lang.Object)
when trying to create an adapter. This also makes Golo confused when only using a closure instead of an adapter (i.e using a closure as a functional interface) where a Comparator is expected.Error stack :
Code :