/*
* Copyright 2014 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.robovm.rt.lambdas;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.Test;
/**
* Tests Java 8 features. It is super sourced so that gwt can be compiles under Java 7.
*
* IMPORTANT: For each test here there must exist the corresponding method in the non super sourced
* version.
*
* Eventually this test will graduate and not be super sourced.
*/
public class Java8Test {
int local = 42;
static abstract class SameClass {
public int method1() { return 10; }
public abstract int method2();
}
interface Lambda<T> {
T run(int a, int b);
}
interface Lambda2<String> {
boolean run(String a, String b);
}
interface Lambda3<String> {
boolean run(String a);
}
class AcceptsLambda<T> {
public T accept(Lambda<T> foo) {
return foo.run(10, 20);
}
public boolean accept2(Lambda2<String> foo) {
return foo.run("a", "b");
}
public boolean accept3(Lambda3<String> foo) {
return foo.run("hello");
}
}
class Pojo {
private final int x;
private final int y;
public Pojo(int x, int y) {
this.x = x;
this.y = y;
}
public int fooInstance(int a, int b) {
return a + b + x + y;
}
}
interface DefaultInterface {
void method1();
// CHECKSTYLE_OFF
default int method2() { return 42; }
default int redeclaredAsAbstract() {
return 88;
}
default Integer addInts(int x, int y) { return x + y; }
default String print() { return "DefaultInterface"; }
// CHECKSTYLE_ON
}
interface DefaultInterface2 {
void method3();
// CHECKSTYLE_OFF
default int method4() { return 23; }
default int redeclaredAsAbstract() {
return 77;
}
// CHECKSTYLE_ON
}
interface DefaultInterfaceSubType extends DefaultInterface {
// CHECKSTYLE_OFF
default int method2() { return 43; }
default String print() {
return "DefaultInterfaceSubType " + DefaultInterface.super.print();
}
// CHECKSTYLE_ON
}
static abstract class DualImplementorSuper implements DefaultInterface {
public void method1() {
}
public abstract int redeclaredAsAbstract();
}
static class DualImplementorBoth extends VirtualUpRef implements DefaultInterface,
DefaultInterface2 {
public void method1() {
}
public void method3() {
}
}
static class DualImplementor extends DualImplementorSuper implements DefaultInterface2 {
public void method3() {
}
public int redeclaredAsAbstract() {
return DefaultInterface2.super.redeclaredAsAbstract();
}
}
// this doesn't implement DefaultInterface, but will provide implementation in subclasses
static class VirtualUpRef {
public int method2() {
return 99;
}
public int redeclaredAsAbstract() {
return 44;
}
}
class Inner {
int local = 22;
public void run() {
assertEquals(94, new AcceptsLambda<Integer>().accept((a,b) -> Java8Test.this.local + local + a + b).intValue());
}
}
static class Static {
static int staticField;
static {
staticField = 99;
}
static Integer staticMethod(int x, int y) { return x + y + staticField; }
}
static class StaticFailIfClinitRuns {
static {
fail("clinit() shouldn't run from just taking a reference to a method");
}
public static Integer staticMethod(int x, int y) {
return null;
}
}
static class DefaultInterfaceImpl implements DefaultInterface {
public void method1() {
}
}
static class DefaultInterfaceImpl2 implements DefaultInterface {
public void method1() {
}
public int method2() {
return 100;
}
}
static class DefaultInterfaceImplVirtualUpRef extends VirtualUpRef implements DefaultInterface {
public void method1() {
}
}
static class DefaultInterfaceImplVirtualUpRefTwoInterfaces extends VirtualUpRef
implements DefaultInterfaceSubType {
public void method1() {
}
// CHECKSTYLE_OFF
public String print() { return "DefaultInterfaceImplVirtualUpRefTwoInterfaces"; }
// CHECKSTYLE_ON
}
@Test public void testLambdaNoCapture() {
assertEquals(30, new AcceptsLambda<Integer>().accept((a, b) -> a + b).intValue());
}
@Test public void testLambdaCaptureLocal() {
int x = 10;
assertEquals(40, new AcceptsLambda<Integer>().accept((a,b) -> x + a + b).intValue());
}
@Test public void testLambdaCaptureLocalWithInnerClass() {
int x = 10;
Lambda<Integer> l = (a,b) -> new Lambda<Integer>() {
@Override public Integer run(int a, int b) {
int t = x;
return t + a + b;
}
}.run(a,b);
assertEquals(40, new AcceptsLambda<Integer>().accept(l).intValue());
}
@Test public void testLambdaCaptureLocalAndField() {
int x = 10;
assertEquals(82, new AcceptsLambda<Integer>().accept((a,b) -> x + local + a + b).intValue());
}
@Test public void testLambdaCaptureLocalAndFieldWithInnerClass() {
int x = 10;
Lambda<Integer> l = (a,b) -> new Lambda<Integer>() {
@Override public Integer run(int j, int k) {
int t = x;
int s = local;
return t + s + a + b;
}
}.run(a,b);
assertEquals(82, new AcceptsLambda<Integer>().accept(l).intValue());
}
@Test public void testCompileLambdaCaptureOuterInnerField() throws Exception {
new Inner().run();
}
@Test public void testStaticReferenceBinding() throws Exception {
assertEquals(129, new AcceptsLambda<Integer>().accept(Static::staticMethod).intValue());
// if this next line runs a clinit, it fails
Lambda l = dummyMethodToMakeCheckStyleHappy(StaticFailIfClinitRuns::staticMethod);
try {
// but now it should fail
l.run(1,2);
fail("Clinit should have run for the first time");
} catch (AssertionError ae) {
// success, it was supposed to throw!
}
}
private static Lambda<Integer> dummyMethodToMakeCheckStyleHappy(Lambda<Integer> l) {
return l;
}
@Test public void testInstanceReferenceBinding() throws Exception {
Pojo instance1 = new Pojo(1, 2);
Pojo instance2 = new Pojo(3, 4);
assertEquals(33, new AcceptsLambda<Integer>().accept(instance1::fooInstance).intValue());
assertEquals(37, new AcceptsLambda<Integer>().accept(instance2::fooInstance).intValue());
}
@Test public void testImplicitQualifierReferenceBinding() throws Exception {
assertFalse(new AcceptsLambda<String>().accept2(String::equalsIgnoreCase));
assertTrue(new AcceptsLambda<String>().accept3("hello world"::contains));
}
@Test public void testConstructorReferenceBinding() {
assertEquals(30, new AcceptsLambda<Pojo>().accept(Pojo::new).fooInstance(0, 0));
}
@Test public void testStaticInterfaceMethod() {
assertEquals(99, (int) Static.staticMethod(0, 0));
}
interface ArrayCtor {
ArrayElem [][][] copy(int i);
}
interface ArrayCtorBoxed {
ArrayElem [][][] copy(Integer i);
}
static class ArrayElem {
}
@Test public void testArrayConstructorReference() {
ArrayCtor ctor = ArrayElem[][][]::new;
ArrayElem[][][] array = ctor.copy(100);
assertEquals(100, array.length);
}
@Test public void testArrayConstructorReferenceBoxed() {
ArrayCtorBoxed ctor = ArrayElem[][][]::new;
ArrayElem[][][] array = ctor.copy(100);
assertEquals(100, array.length);
}
interface ThreeArgs {
int foo(int x, int y, int z);
}
interface ThreeVarArgs {
int foo(int x, int y, int... z);
}
public static int addMany(int x, int y, int... nums) {
int sum = x + y;
for (int num : nums) {
sum += num;
}
return sum;
}
@Test public void testVarArgsReferenceBinding() {
ThreeArgs t = Java8Test::addMany;
assertEquals(6, t.foo(1,2,3));
}
@Test public void testVarArgsPassthroughReferenceBinding() {
ThreeVarArgs t = Java8Test::addMany;
assertEquals(6, t.foo(1,2,3));
}
@Test public void testVarArgsPassthroughReferenceBindingProvidedArray() {
ThreeVarArgs t = Java8Test::addMany;
assertEquals(6, t.foo(1,2, new int[] {3}));
}
interface I {
int foo(Integer i);
}
@Test public void testSuperReferenceExpression() {
class Y {
int foo(Integer i) {
return 42;
}
}
class X extends Y {
int foo(Integer i) {
return 23;
}
int goo() {
I i = super::foo;
return i.foo(0);
}
}
assertEquals(42, new X().goo());
}
// static class X2 {
// protected int field;
// void foo() {
// int local;
// static class Y extends X2 {
// static class Z extends X2 {
// void f() {
// Ctor c = X2::new;
// X2 x = c.makeX(123456);
// assertEquals(123456, x.field);
// c = Y::new;
// x = c.makeX(987654);
// x = new Y(987654);
// assertEquals(987655, x.field);
// c = Z::new;
// x = c.makeX(456789);
// x = new Z(456789);
// assertEquals(456791, x.field);
// }
// private Z(int z) {
// super(z + 2);
// }
// Z() {
// }
// }
//
// private Y(int y) {
// super(y + 1);
// }
//
// private Y() {
// }
// }
// new Y().new Z().f();
// }
//
// private X2(int x) {
// this.field = x;
// }
// X2() {
// }
// }
@Test public void testSuperReferenceExpressionWithVarArgs() {
class Base {
int foo(Object... objects) {
return 0;
}
}
class X extends Base {
int foo(Object... objects) {
throw new AssertionError();
}
void goo() {
I i = super::foo;
i.foo(10);
}
}
new X().goo();
}
// interface Ctor {
// X2 makeX(int x);
// }
// RoboVM Note: uncomment once the nested classes
// situation above is resolved.
// @Test public void testPrivateConstructorReference() {
// new X2().foo();
// }
@Test public void testDefaultInterfaceMethod() {
assertEquals(42, new DefaultInterfaceImpl().method2());
}
@Test public void testDefaultInterfaceMethodVirtualUpRef() {
assertEquals(99, new DefaultInterfaceImplVirtualUpRef().method2());
assertEquals(99, new DefaultInterfaceImplVirtualUpRefTwoInterfaces().method2());
// RoboVM Note: this crashes appearently due a big in the Eclipse compiler
// assertEquals("SimpleB", new org.robovm.rt.lambdas.package3.SimpleC().m());
assertEquals("SimpleASimpleB", new org.robovm.rt.lambdas.package1.SimpleD().m());
}
@Test public void testDefaultInterfaceMethodMultiple() {
assertEquals(42, new DualImplementor().method2());
assertEquals(23, new DualImplementor().method4());
assertEquals(77, new DualImplementor().redeclaredAsAbstract());
assertEquals(44, new DualImplementorBoth().redeclaredAsAbstract());
DefaultInterfaceImplVirtualUpRefTwoInterfaces instanceImplementInterfaceSubType =
new DefaultInterfaceImplVirtualUpRefTwoInterfaces();
DefaultInterfaceSubType interfaceSubType1 = instanceImplementInterfaceSubType;
assertEquals("DefaultInterfaceImplVirtualUpRefTwoInterfaces",
instanceImplementInterfaceSubType.print());
assertEquals("DefaultInterfaceImplVirtualUpRefTwoInterfaces", interfaceSubType1.print());
DefaultInterfaceSubType interfaceSubType2 = new DefaultInterfaceSubType() {
@Override
public void method1() { }
};
assertEquals("DefaultInterfaceSubType DefaultInterface",
interfaceSubType2.print());
DefaultInterfaceSubType interfaceSubType3 = () -> { };
assertEquals("DefaultInterfaceSubType DefaultInterface",
interfaceSubType3.print());
}
@Test public void testDefenderMethodByInterfaceInstance() {
DefaultInterfaceImpl2 interfaceImpl2 = new DefaultInterfaceImpl2();
DefaultInterface interface1 = interfaceImpl2;
assertEquals(100, interfaceImpl2.method2());
assertEquals(100, interface1.method2());
}
@Test public void testDefaultMethodReference() {
DefaultInterfaceImplVirtualUpRef x = new DefaultInterfaceImplVirtualUpRef();
assertEquals(30, (int) new AcceptsLambda<Integer>().accept(x::addInts));
}
interface InterfaceWithTwoDefenderMethods {
// CHECKSTYLE_OFF
default String foo() { return "interface.foo"; }
default String bar() { return this.foo() + " " + foo(); }
// CHECKSTYLE_ON
}
class ClassImplementOneDefenderMethod implements InterfaceWithTwoDefenderMethods {
public String foo() {
return "class.foo";
}
}
@Test public void testThisRefInDefenderMethod() {
ClassImplementOneDefenderMethod c = new ClassImplementOneDefenderMethod();
InterfaceWithTwoDefenderMethods i1 = c;
InterfaceWithTwoDefenderMethods i2 = new InterfaceWithTwoDefenderMethods() { };
assertEquals("class.foo class.foo", c.bar());
assertEquals("class.foo class.foo", i1.bar());
assertEquals("interface.foo interface.foo", i2.bar());
}
interface InterfaceImplementOneDefenderMethod extends InterfaceWithTwoDefenderMethods {
// CHECKSTYLE_OFF
default String foo() { return "interface1.foo"; }
// CHECKSTYLE_ON
}
interface InterfaceImplementZeroDefenderMethod extends InterfaceWithTwoDefenderMethods {
}
class ClassImplementsTwoInterfaces implements InterfaceImplementOneDefenderMethod,
InterfaceImplementZeroDefenderMethod {
}
@Test public void testClassImplementsTwoInterfacesWithSameDefenderMethod() {
ClassImplementsTwoInterfaces c = new ClassImplementsTwoInterfaces();
assertEquals("interface1.foo", c.foo());
}
abstract class AbstractClass implements InterfaceWithTwoDefenderMethods {
}
class Child1 extends AbstractClass {
public String foo() {
return super.foo() + " child1.foo";
}
}
class Child2 extends AbstractClass {
}
@Test public void testAbstractClassImplementsInterface() {
Child1 child1 = new Child1();
Child2 child2 = new Child2();
assertEquals("interface.foo child1.foo", child1.foo());
assertEquals("interface.foo", child2.foo());
}
interface InterfaceI {
// CHECKSTYLE_OFF
default String print() { return "interface1"; }
// CHECKSTYLE_ON
}
interface InterfaceII {
// CHECKSTYLE_OFF
default String print() { return "interface2"; }
// CHECKSTYLE_ON
}
class ClassI {
public String print() {
return "class1";
}
}
class ClassII extends ClassI implements InterfaceI, InterfaceII {
public String print() {
return super.print() + " " + InterfaceI.super.print() + " " + InterfaceII.super.print();
}
}
@Test public void testSuperRefInDefenderMethod() {
ClassII c = new ClassII();
assertEquals("class1 interface1 interface2", c.print());
}
interface II {
// CHECKSTYLE_OFF
default String fun() { return "fun() in i: " + this.foo(); };
default String foo() { return "foo() in i.\n"; };
// CHECKSTYLE_ON
}
interface JJ extends II {
// CHECKSTYLE_OFF
default String fun() {
return "fun() in j: " + this.foo() + II.super.fun();
};
default String foo() { return "foo() in j.\n"; }
// CHECKSTYLE_ON
}
class AA {
public String fun() {
return "fun() in a: " + this.foo();
}
public String foo() {
return "foo() in a.\n";
}
}
class BB extends AA implements JJ {
public String fun() {
return "fun() in b: " + this.foo() + super.fun() + JJ.super.fun();
}
public String foo() {
return "foo() in b.\n";
}
}
class CC extends BB implements JJ {
public String fun() {
return "fun() in c: " + super.fun();
}
}
@Test
public void testSuperThisRefsInDefenderMethod() {
CC c = new CC();
II i1 = c;
JJ j1 = c;
BB b = new BB();
II i2 = b;
JJ j2 = b;
JJ j3 = new JJ() { };
II i3 = j3;
II i4 = new II() { };
String c_fun = "fun() in c: fun() in b: foo() in b.\n"
+ "fun() in a: foo() in b.\n"
+ "fun() in j: foo() in b.\n"
+ "fun() in i: foo() in b.\n";
String b_fun = "fun() in b: foo() in b.\n"
+ "fun() in a: foo() in b.\n"
+ "fun() in j: foo() in b.\n"
+ "fun() in i: foo() in b.\n";
String j_fun = "fun() in j: foo() in j.\n"
+ "fun() in i: foo() in j.\n";
String i_fun = "fun() in i: foo() in i.\n";
assertEquals(c_fun, c.fun());
assertEquals(c_fun, i1.fun());
assertEquals(c_fun, j1.fun());
assertEquals(b_fun, b.fun());
assertEquals(b_fun, i2.fun());
assertEquals(b_fun, j2.fun());
assertEquals(j_fun, j3.fun());
assertEquals(j_fun, i3.fun());
assertEquals(i_fun, i4.fun());
}
interface OuterInterface {
// CHECKSTYLE_OFF
default String m() {
return "I.m;" + new InnerClass().n();
}
default String n() {
return "I.n;" + this.m();
}
// CHECKSTYLE_ON
class InnerClass {
public String n() {
return "A.n;" + m();
}
public String m() {
return "A.m;";
}
}
}
class OuterClass {
public String m() {
return "B.m;";
}
public String n1() {
OuterInterface i = new OuterInterface() { };
return "B.n1;" + i.n() + OuterClass.this.m();
}
public String n2() {
OuterInterface i = new OuterInterface() {
@Override
public String n() {
return this.m() + OuterClass.this.m();
}
};
return "B.n2;" + i.n() + OuterClass.this.m();
}
}
@Test public void testNestedInterfaceClass() {
OuterClass outerClass = new OuterClass();
assertEquals("B.n1;I.n;I.m;A.n;A.m;B.m;", outerClass.n1());
assertEquals("B.n2;I.m;A.n;A.m;B.m;B.m;", outerClass.n2());
}
class EmptyA { }
interface EmptyI { }
interface EmptyJ { }
class EmptyB extends EmptyA implements EmptyI { }
class EmptyC extends EmptyA implements EmptyI, EmptyJ { }
@Test public void testBaseIntersectionCast() {
EmptyA localB = new EmptyB();
EmptyA localC = new EmptyC();
EmptyB b2BI = (EmptyB & EmptyI) localB;
EmptyC c2CIJ = (EmptyC & EmptyI & EmptyJ) localC;
EmptyI ii1 = (EmptyB & EmptyI) localB;
EmptyI ii2 = (EmptyC & EmptyI) localC;
EmptyI ii3 = (EmptyC & EmptyJ) localC;
EmptyI ii4 = (EmptyC & EmptyI & EmptyJ) localC;
EmptyJ jj1 = (EmptyC & EmptyI & EmptyJ) localC;
EmptyJ jj2 = (EmptyC & EmptyI) localC;
EmptyJ jj3 = (EmptyC & EmptyJ) localC;
EmptyJ jj4 = (EmptyI & EmptyJ) localC;
try {
EmptyC b2CIJ = (EmptyC & EmptyI & EmptyJ) localB;
fail("Should have thrown a ClassCastException");
} catch (ClassCastException e) {
// Expected.
}
try {
EmptyB c2BI = (EmptyB & EmptyI) localC;
fail("Should have thrown a ClassCastException");
} catch (ClassCastException e) {
// Expected.
}
try {
EmptyJ jj = (EmptyB & EmptyJ) localB;
fail("Should have thrown a ClassCastException");
} catch (ClassCastException e) {
// Expected.
}
}
interface SimpleI {
int fun();
}
interface SimpleJ {
int foo();
int bar();
}
interface SimpleK {
}
@Test public void testIntersectionCastWithLambdaExpr() {
SimpleI simpleI1 = (SimpleI & EmptyI) () -> { return 11; };
assertEquals(11, simpleI1.fun());
SimpleI simpleI2 = (EmptyI & SimpleI) () -> { return 22; };
assertEquals(22, simpleI2.fun());
EmptyI emptyI = (EmptyI & SimpleI) () -> { return 33; };
try {
// RoboVM Note: this fails when constructing the callsite
// because EmptyA is not an interface
// ((EmptyA & SimpleI) () -> { return 33; }).fun();
// fail("Should have thrown a ClassCastException");
} catch (ClassCastException e) {
// expected.
}
try {
// RoboVM Note: no classcast exception is thrown
// ((SimpleI & SimpleJ) () -> { return 44; }).fun();
// fail("Should have thrown a ClassCastException");
} catch (ClassCastException e) {
// expected.
}
try {
// RoboVM Note: this creates an AbstractMethodError
// ((SimpleI & SimpleJ) () -> { return 44; }).foo();
// fail("Should have thrown a ClassCastException");
} catch (ClassCastException e) {
// expected.
}
try {
// RoboVM Note: this creates an AbstractMethodError
// ((SimpleI & SimpleJ) () -> { return 44; }).bar();
// fail("Should have thrown a ClassCastException");
} catch (ClassCastException e) {
// expected.
}
assertEquals(55, ((SimpleI & SimpleK) () -> { return 55; }).fun());
}
class SimpleA {
public int bar() {
return 11;
}
}
class SimpleB extends SimpleA implements SimpleI {
public int fun() {
return 22;
}
}
class SimpleC extends SimpleA implements SimpleI {
public int fun() {
return 33;
}
public int bar() {
return 44;
}
}
@Test public void testIntersectionCastPolymorphism() {
SimpleA bb = new SimpleB();
assertEquals(22, ((SimpleB & SimpleI) bb).fun());
assertEquals(11, ((SimpleB & SimpleI) bb).bar());
SimpleA cc = new SimpleC();
assertEquals(33, ((SimpleC & SimpleI) cc).fun());
assertEquals(44, ((SimpleC & SimpleI) cc).bar());
assertEquals(33, ((SimpleA & SimpleI) cc).fun());
SimpleI ii = (SimpleC & SimpleI) cc;
assertEquals(33, ii.fun());
}
interface ClickHandler {
int onClick(int a);
}
private int addClickHandler(ClickHandler clickHandler) {
return clickHandler.onClick(1);
}
private int addClickHandler(int a) {
return addClickHandler(x -> { int temp = a; return temp; });
}
@Test public void testLambdaCaptureParameter() {
assertEquals(2, addClickHandler(2));
}
interface TestLambda_Inner {
void f();
}
interface TestLambda_Outer {
void accept(TestLambda_Inner t);
}
public void testLambda_call(TestLambda_Outer a) {
a.accept(() -> { });
}
@Test public void testLambdaNestingCaptureLocal() {
int[] success = new int[] {0};
testLambda_call(sam1 -> { testLambda_call(sam2 -> { success[0] = 10; }); });
assertEquals(10, success[0]);
}
static class TestLambda_Class {
public int[] s = new int[] {0};
public void call(TestLambda_Outer a) {
a.accept(() -> { });
}
class TestLambda_InnerClass {
public int[] s = new int[] {0};
public int test() {
int[] s = new int[] {0};
TestLambda_Class.this.call(
sam0 -> TestLambda_Class.this.call(
sam1 -> {
TestLambda_Class.this.call(
sam2 -> {
TestLambda_Class.this.s[0] = 10;
this.s[0] = 20;
s[0] = 30;
});
}));
return s[0];
}
}
}
@Test public void testLambdaNestingCaptureField() {
TestLambda_Class a = new TestLambda_Class();
a.call(sam1 -> { a.call(sam2 -> { a.s[0] = 20; }); });
assertEquals(20, a.s[0]);
}
@Test public void testLambdaMultipleNestingCaptureFieldAndLocal() {
TestLambda_Class a = new TestLambda_Class();
TestLambda_Class b = new TestLambda_Class();
int [] s = new int [] {0};
b.call(sam0 -> a.call(sam1 -> { a.call(sam2 -> { a.s[0] = 20; b.s[0] = 30; s[0] = 40; }); }));
assertEquals(20, a.s[0]);
assertEquals(30, b.s[0]);
assertEquals(40, s[0]);
}
@Test public void testLambdaMultipleNestingCaptureFieldAndLocalInnerClass() {
TestLambda_Class a = new TestLambda_Class();
TestLambda_Class.TestLambda_InnerClass b = a.new TestLambda_InnerClass();
int result = b.test();
assertEquals(10, a.s[0]);
assertEquals(20, b.s[0]);
assertEquals(30, result);
}
static class TestMF_A {
public static String getId() {
return "A";
}
public int getIdx() {
return 1;
}
}
static class TestMF_B {
public static String getId() {
return "B";
}
public int getIdx() {
return 2;
}
}
interface Function<T> {
T apply();
}
private String f(Function<String> arg) {
return arg.apply();
}
private int g(Function<Integer> arg) {
return arg.apply().intValue();
}
@Test public void testMethodRefWithSameName() {
assertEquals("A", f(TestMF_A::getId));
assertEquals("B", f(TestMF_B::getId));
TestMF_A a = new TestMF_A();
TestMF_B b = new TestMF_B();
assertEquals(1, g(a::getIdx));
assertEquals(2, g(b::getIdx));
}
// Test particular scenarios involving multiple path to inherit defaults.
interface ITop {
default String m() {
return "ITop.m()";
}
}
interface IRight extends ITop {
default String m() {
return "IRight.m()";
}
}
interface ILeft extends ITop { }
@Test
public void testMultipleDefaults_fromInterfaces_left() {
class A implements ILeft, IRight { }
assertEquals("IRight.m()", new A().m());
}
@Test public void testMultipleDefaults_fromInterfaces_right() {
class A implements IRight, ILeft { }
assertEquals("IRight.m()", new A().m());
}
@Test
public void testMultipleDefaults_superclass_left() {
class A implements ITop { }
class B extends A implements ILeft, IRight { }
assertEquals("IRight.m()", new B().m());
}
@Test public void testMultipleDefaults_superclass_right() {
class A implements ITop { }
class B extends A implements IRight, ILeft { }
assertEquals("IRight.m()", new B().m());
}
interface InterfaceWithThisReference {
default String n() {
return "default n";
}
default String callNUnqualified() {
class Super implements InterfaceWithThisReference {
public String n() {
return "super n";
}
}
return new Super() {
public String callNUnqualified() {
return "Object " + n();
}
}.callNUnqualified();
}
default String callNWithThis() {
class Super implements InterfaceWithThisReference {
public String n() {
return "super n";
}
}
return new Super() {
public String callNWithThis() {
return "Object " + this.n();
}
}.callNWithThis();
}
default String callNWithInterfaceThis() {
class Super implements InterfaceWithThisReference {
public String n() {
return "super n";
}
}
return new Super() {
public String callNWithInterfaceThis() {
// In this method this has interface Test as its type, but it refers to outer n();
return "Object " + InterfaceWithThisReference.this.n();
}
}.callNWithInterfaceThis();
}
default String callNWithSuper() {
class Super implements InterfaceWithThisReference {
public String n() {
return "super n";
}
}
return new Super() {
public String callNWithSuper() {
// In this method this has interface Test as its type.
return "Object " + super.n();
}
}.callNWithSuper();
}
default String callNWithInterfaceSuper() {
return new InterfaceWithThisReference() {
public String n() {
return "this n";
}
public String callNWithInterfaceSuper() {
// In this method this has interface Test as its type and refers to default n();
return "Object " + InterfaceWithThisReference.super.n();
}
}.callNWithInterfaceSuper();
}
}
@Test public void testInterfaceThis() {
class A implements InterfaceWithThisReference {
public String n() {
return "n";
}
}
assertEquals("Object super n", new A().callNUnqualified());
assertEquals("Object super n", new A().callNWithThis());
assertEquals("Object n", new A().callNWithInterfaceThis());
assertEquals("Object super n", new A().callNWithSuper());
assertEquals("Object default n", new A().callNWithInterfaceSuper());
}
}