| /* |
| * 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 com.google.gwt.dev.jjs.test; |
| |
| import com.google.gwt.core.client.GwtScriptOnly; |
| import com.google.gwt.core.client.JavaScriptObject; |
| import com.google.gwt.core.client.JsonUtils; |
| import com.google.gwt.dev.jjs.test.defaultmethods.ImplementsWithDefaultMethodAndStaticInitializer; |
| import com.google.gwt.dev.jjs.test.defaultmethods.SomeClass; |
| import com.google.gwt.junit.client.GWTTestCase; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.function.BiFunction; |
| import java.util.function.IntFunction; |
| import java.util.function.Predicate; |
| import java.util.function.Supplier; |
| import java.util.stream.Collectors; |
| |
| import jsinterop.annotations.JsFunction; |
| import jsinterop.annotations.JsOverlay; |
| import jsinterop.annotations.JsPackage; |
| import jsinterop.annotations.JsProperty; |
| import jsinterop.annotations.JsType; |
| |
| /** |
| * 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. |
| */ |
| @GwtScriptOnly |
| public class Java8Test extends GWTTestCase { |
| 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 |
| } |
| |
| @Override |
| public String getModuleName() { |
| return "com.google.gwt.dev.jjs.Java8Test"; |
| } |
| |
| public void testLambdaNoCapture() { |
| assertEquals(30, new AcceptsLambda<Integer>().accept((a, b) -> a + b).intValue()); |
| } |
| |
| public void testLambdaCaptureLocal() { |
| int x = 10; |
| assertEquals(40, new AcceptsLambda<Integer>().accept((a,b) -> x + a + b).intValue()); |
| } |
| |
| 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()); |
| } |
| |
| public void testLambdaCaptureLocalAndField() { |
| int x = 10; |
| assertEquals(82, new AcceptsLambda<Integer>().accept((a,b) -> x + local + a + b).intValue()); |
| } |
| |
| 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()); |
| } |
| |
| public void testCompileLambdaCaptureOuterInnerField() throws Exception { |
| new Inner().run(); |
| } |
| |
| 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; |
| } |
| |
| 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()); |
| } |
| |
| public void testImplicitQualifierReferenceBinding() throws Exception { |
| assertFalse(new AcceptsLambda<String>().accept2(String::equalsIgnoreCase)); |
| assertTrue(new AcceptsLambda<String>().accept3("hello world"::contains)); |
| } |
| |
| public void testConstructorReferenceBinding() { |
| assertEquals(30, new AcceptsLambda<Pojo>().accept(Pojo::new).fooInstance(0, 0)); |
| } |
| |
| 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 { |
| } |
| |
| public void testArrayConstructorReference() { |
| ArrayCtor ctor = ArrayElem[][][]::new; |
| ArrayElem[][][] array = ctor.copy(100); |
| assertEquals(100, array.length); |
| } |
| |
| 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; |
| } |
| |
| public void testVarArgsReferenceBinding() { |
| ThreeArgs t = Java8Test::addMany; |
| assertEquals(6, t.foo(1,2,3)); |
| } |
| |
| public void testVarArgsPassthroughReferenceBinding() { |
| ThreeVarArgs t = Java8Test::addMany; |
| assertEquals(6, t.foo(1,2,3)); |
| } |
| |
| public void testVarArgsPassthroughReferenceBindingProvidedArray() { |
| ThreeVarArgs t = Java8Test::addMany; |
| assertEquals(6, t.foo(1,2, new int[] {3})); |
| } |
| |
| interface I { |
| int foo(Integer i); |
| } |
| |
| 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()); |
| } |
| |
| public void testQualifiedSuperReferenceExpression() { |
| class Y { |
| int foo(Integer i) { |
| return 42; |
| } |
| } |
| |
| class X extends Y { |
| int foo(Integer i) { |
| return 23; |
| } |
| |
| int goo() { |
| I i = X.super::foo; |
| return i.foo(0); |
| } |
| } |
| |
| assertEquals(42, new X().goo()); |
| } |
| |
| static class X2 { |
| protected int field; |
| void foo() { |
| int local; |
| class Y extends X2 { |
| 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() { |
| } |
| } |
| |
| 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); |
| } |
| |
| public void testPrivateConstructorReference() { |
| new X2().foo(); |
| } |
| |
| public void testDefaultInterfaceMethod() { |
| assertEquals(42, new DefaultInterfaceImpl().method2()); |
| } |
| |
| public void testDefaultInterfaceMethodVirtualUpRef() { |
| assertEquals(99, new DefaultInterfaceImplVirtualUpRef().method2()); |
| assertEquals(99, new DefaultInterfaceImplVirtualUpRefTwoInterfaces().method2()); |
| assertEquals("SimpleB", new com.google.gwt.dev.jjs.test.package3.SimpleC().m()); |
| assertEquals("SimpleASimpleB", new com.google.gwt.dev.jjs.test.package1.SimpleD().m()); |
| } |
| |
| 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()); |
| } |
| |
| public void testDefenderMethodByInterfaceInstance() { |
| DefaultInterfaceImpl2 interfaceImpl2 = new DefaultInterfaceImpl2(); |
| DefaultInterface interface1 = interfaceImpl2; |
| assertEquals(100, interfaceImpl2.method2()); |
| assertEquals(100, interface1.method2()); |
| } |
| |
| 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"; |
| } |
| } |
| |
| 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 { |
| } |
| |
| 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 { |
| } |
| |
| 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(); |
| } |
| } |
| |
| 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(); |
| } |
| } |
| |
| 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(); |
| } |
| } |
| 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 { } |
| 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 SimpleK { |
| } |
| public void testIntersectionCastWithLambdaExpr() { |
| SimpleI simpleI1 = (SimpleI & EmptyI) () -> 11; |
| assertEquals(11, simpleI1.fun()); |
| SimpleI simpleI2 = (EmptyI & SimpleI) () -> 22; |
| assertEquals(22, simpleI2.fun()); |
| EmptyI emptyI = (EmptyI & SimpleI) () -> 33; |
| assertEquals(55, ((SimpleI & SimpleK) () -> 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; |
| } |
| } |
| |
| 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; }); |
| } |
| 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(() -> { }); |
| } |
| public void testLambdaNestingCaptureLocal() { |
| int[] success = new int[] {0}; |
| testLambda_call(sam1 -> { testLambda_call(sam2 -> { success[0] = 10; }); }); |
| assertEquals(10, success[0]); |
| } |
| |
| public void testLambdaNestingInAnonymousCaptureLocal() { |
| int[] x = new int[] {42}; |
| new Runnable() { |
| public void run() { |
| Lambda<Integer> l = (a, b) -> x[0] = x[0] + a + b; |
| l.run(1, 2); |
| } |
| }.run(); |
| assertEquals(45, x[0]); |
| } |
| |
| public void testLambdaNestingInMultipleMixedAnonymousCaptureLocal() { |
| // checks that lambda has access to local variable and arguments when placed in mixed scopes |
| // Local Class -> Local Class -> Local Anonymous -> lambda -> Local Anonymous |
| class A { |
| int a() { |
| int[] x = new int[] {42}; |
| class B { |
| void b() { |
| I i = new I() { |
| public int foo(Integer arg) { |
| Runnable r = () -> { |
| new Runnable() { |
| public void run() { |
| Lambda<Integer> l = (a, b) -> x[0] = x[0] + a + b + arg; |
| l.run(1, 2); |
| } |
| }.run(); |
| }; |
| r.run(); |
| return x[0]; |
| } |
| }; |
| i.foo(1); |
| } |
| } |
| B b = new B(); |
| b.b(); |
| return x[0]; |
| } |
| } |
| A a = new A(); |
| assertEquals(46, a.a()); |
| } |
| |
| public void testLambdaNestingInMultipleMixedAnonymousCaptureLocal_withInterference() { |
| // checks that lambda has access to NEAREST local variable and arguments when placed in mixed |
| // scopes Local Class -> Local Class -> Local Anonymous -> lambda -> Local Anonymous |
| class A { |
| int a() { |
| int[] x = new int[] {42}; |
| class B { |
| int b() { |
| int[] x = new int[] {22}; |
| I i = new I() { |
| public int foo(Integer arg) { |
| Runnable r = () -> { |
| new Runnable() { |
| public void run() { |
| Lambda<Integer> l = (a, b) -> x[0] = x[0] + a + b + arg; |
| l.run(1, 2); |
| } |
| }.run(); |
| }; |
| r.run(); |
| return x[0]; |
| } |
| }; |
| return i.foo(1); |
| } |
| } |
| B b = new B(); |
| return b.b(); |
| } |
| } |
| A a = new A(); |
| assertEquals(26, a.a()); |
| } |
| |
| public void testLambdaNestingInMultipleMixedAnonymousCaptureLocalAndField() { |
| // checks that lambda has access to local variable, field and arguments when placed in mixed |
| // scopes - Local Class -> Local Class -> Local Anonymous -> lambda -> Local Anonymous |
| class A { |
| int fA = 1; |
| |
| int a() { |
| int[] x = new int[] {42}; |
| class B { |
| int fB = 2; |
| |
| int b() { |
| I i = new I() { |
| int fI = 3; |
| |
| public int foo(Integer arg) { |
| Runnable r = () -> { |
| new Runnable() { |
| public void run() { |
| Lambda<Integer> l = (a, b) -> x[0] = x[0] + a + b + arg + fA + fB + fI; |
| l.run(1, 2); |
| } |
| }.run(); |
| }; |
| r.run(); |
| return x[0]; |
| } |
| }; |
| return i.foo(1); |
| } |
| } |
| B b = new B(); |
| return b.b(); |
| } |
| } |
| A a = new A(); |
| assertEquals(52, a.a()); |
| } |
| |
| public void testLambdaNestingInMultipleAnonymousCaptureLocal() { |
| // checks that lambda has access to local variable and arguments when placed in local anonymous |
| // class with multile nesting |
| int[] x = new int[] {42}; |
| int result = new I() { |
| public int foo(Integer i1) { |
| return new I() { |
| public int foo(Integer i2) { |
| return new I() { |
| public int foo(Integer i3) { |
| Lambda<Integer> l = (a, b) -> x[0] = x[0] + a + b + i1 + i2 + i3; |
| return l.run(1, 2); |
| } |
| }.foo(3); |
| } |
| }.foo(2); |
| } |
| }.foo(1); |
| assertEquals(51, x[0]); |
| } |
| |
| static class TestLambda_ClassA { |
| int[] f = new int[] {42}; |
| |
| class B { |
| void m() { |
| Runnable r = () -> f[0] = f[0] + 1; |
| r.run(); |
| } |
| } |
| |
| int a() { |
| B b = new B(); |
| b.m(); |
| return f[0]; |
| } |
| } |
| |
| public void testLambdaNestingCaptureField_InnerClassCapturingOuterClassVariable() { |
| TestLambda_ClassA a = new TestLambda_ClassA(); |
| assertEquals(43, a.a()); |
| } |
| |
| public void testInnerClassCaptureLocalFromOuterLambda() { |
| int[] x = new int[] {42}; |
| Lambda<Integer> l = (a, b) -> { |
| int[] x1 = new int[] {32}; |
| Lambda<Integer> r = (rA, rB) -> { |
| int[] x2 = new int[] {22}; |
| I i = new I() { |
| public int foo(Integer arg) { |
| x1[0] = x1[0] + 1; |
| x[0] = x[0] + 1; |
| return x2[0] = x2[0] + rA + rB + a + b; |
| } |
| }; |
| return i.foo(1); |
| }; |
| return r.run(3, 4) + x1[0]; |
| }; |
| |
| // x1[0](32) + 1 + x2[0](22) + rA(3) + rB(4) + a(1) + b(2) |
| assertEquals(65, l.run(1, 2).intValue()); |
| assertEquals(43, x[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]; |
| } |
| } |
| } |
| |
| public void testLambdaNestingCaptureField() { |
| TestLambda_Class a = new TestLambda_Class(); |
| a.call(sam1 -> { a.call(sam2 -> { a.s[0] = 20; }); }); |
| assertEquals(20, a.s[0]); |
| } |
| |
| 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]); |
| } |
| |
| 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(); |
| } |
| |
| 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 { } |
| |
| public void testMultipleDefaults_fromInterfaces_left() { |
| class A implements ILeft, IRight { } |
| |
| assertEquals("IRight.m()", new A().m()); |
| } |
| |
| public void testMultipleDefaults_fromInterfaces_right() { |
| class A implements IRight, ILeft { } |
| |
| assertEquals("IRight.m()", new A().m()); |
| } |
| |
| public void testMultipleDefaults_superclass_left() { |
| class A implements ITop { } |
| class B extends A implements ILeft, IRight { } |
| |
| assertEquals("IRight.m()", new B().m()); |
| } |
| |
| public void testMultipleDefaults_superclass_right() { |
| class A implements ITop { } |
| class B extends A implements IRight, ILeft { } |
| |
| assertEquals("IRight.m()", new B().m()); |
| } |
| |
| static class DefaultTrumpsOverSyntheticAbstractStub { |
| interface SuperInterface { |
| String m(); |
| } |
| |
| interface SubInterface extends SuperInterface { |
| default String m() { |
| return "SubInterface.m()"; |
| } |
| } |
| } |
| |
| public void testMultipleDefaults_defaultShadowsOverSyntheticAbstractStub() { |
| abstract class A implements DefaultTrumpsOverSyntheticAbstractStub.SuperInterface { } |
| class B extends A implements DefaultTrumpsOverSyntheticAbstractStub.SubInterface { } |
| |
| assertEquals("SubInterface.m()", new B().m()); |
| } |
| |
| static class DefaultTrumpsOverDefaultOnSuperAbstract { |
| interface SuperInterface { |
| default String m() { |
| return "SuperInterface.m()"; |
| } |
| } |
| |
| interface SubInterface extends SuperInterface { |
| default String m() { |
| return "SubInterface.m()"; |
| } |
| } |
| } |
| |
| public void testMultipleDefaults_defaultShadowsOverDefaultOnSuperAbstract() { |
| abstract class A implements DefaultTrumpsOverDefaultOnSuperAbstract.SuperInterface { } |
| class B extends A implements DefaultTrumpsOverDefaultOnSuperAbstract.SubInterface { } |
| |
| assertEquals("SubInterface.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(); |
| } |
| } |
| |
| 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()); |
| } |
| |
| private static List<String> initializationOrder; |
| |
| private static int get(String s) { |
| initializationOrder.add(s); |
| return 1; |
| } |
| |
| interface A1 { |
| int fa1 = get("A1"); |
| |
| default void a1() { } |
| } |
| |
| interface A2 { |
| int fa2 = get("A2"); |
| |
| default void a2() { } |
| } |
| |
| interface A3 { |
| int fa3 = get("A3"); |
| |
| default void a3() { } |
| } |
| |
| interface B1 extends A1 { |
| int fb1 = get("B1"); |
| |
| default void b1() { } |
| } |
| |
| interface B2 extends A2 { |
| int fb2 = get("B2"); |
| |
| default void b2() { } |
| } |
| |
| interface B3 extends A3 { |
| int fb3 = get("B3"); |
| } |
| |
| static class C implements B1, A2 { |
| static { |
| get("C"); |
| } |
| } |
| |
| static class D extends C implements B2, B3 { |
| static { |
| get("D"); |
| } |
| } |
| |
| public void testInterfaceWithDefaultMethodsInitialization() { |
| initializationOrder = new ArrayList<String>(); |
| new D(); |
| assertContentsInOrder(initializationOrder, "A1", "B1", "A2", "C", "B2", "A3", "D"); |
| } |
| |
| /** |
| * Regression test for issue 9214. |
| */ |
| interface P<T> { |
| boolean apply(T obj); |
| } |
| |
| static class B { |
| public boolean getTrue() { |
| return true; |
| } |
| } |
| private static <T> String getClassName(T obj) { |
| return obj.getClass().getSimpleName(); |
| } |
| |
| public void testMethodReference_generics() { |
| P<B> p = B::getTrue; |
| assertTrue(p.apply(new B())); |
| // The next two method references must result in two different lambda implementations due |
| // to generics, see bug # 9333. |
| MyFunction1<B, String> f1 = Java8Test::getClassName; |
| MyFunction1<Double, String> f2 = Java8Test::getClassName; |
| |
| assertEquals(B.class.getSimpleName(), f1.apply(new B())); |
| assertEquals(Double.class.getSimpleName(), f2.apply(new Double(2))); |
| } |
| |
| public void testDefaultMethod_staticInitializer() { |
| SomeClass.initializationOrder = new ArrayList<String>(); |
| Object object = ImplementsWithDefaultMethodAndStaticInitializer.someClass; |
| assertContentsInOrder(SomeClass.initializationOrder, "1", "2", "3", "4"); |
| } |
| |
| private void assertContentsInOrder(Iterable<String> contents, String... elements) { |
| assertEquals(Arrays.asList(elements).toString(), contents.toString()); |
| } |
| |
| @JsType(isNative = true) |
| interface NativeJsTypeInterfaceWithStaticInitializationAndFieldAccess { |
| @JsOverlay |
| Object object = new Integer(3); |
| } |
| |
| @JsType(isNative = true) |
| interface NativeJsTypeInterfaceWithStaticInitializationAndStaticOverlayMethod { |
| @JsOverlay |
| Object object = new Integer(4); |
| |
| @JsOverlay |
| static Object getObject() { |
| return object; |
| } |
| } |
| |
| @JsType(isNative = true) |
| interface NativeJsTypeInterfaceWithStaticInitializationAndInstanceOverlayMethod { |
| @JsOverlay |
| Object object = new Integer(5); |
| |
| int getA(); |
| |
| @JsOverlay |
| default Object getObject() { |
| return ((int) object) + this.getA(); |
| } |
| } |
| |
| private native NativeJsTypeInterfaceWithStaticInitializationAndInstanceOverlayMethod |
| createNativeJsTypeInterfaceWithStaticInitializationAndInstanceOverlayMethod() /*-{ |
| return { getA: function() { return 1; } }; |
| }-*/; |
| |
| @JsType(isNative = true) |
| interface NativeJsTypeInterfaceWithStaticInitialization { |
| @JsOverlay |
| Object object = new Integer(6); |
| } |
| |
| @JsType(isNative = true) |
| interface NativeJsTypeInterfaceWithComplexStaticInitialization { |
| @JsOverlay |
| Object object = (Integer) (((int) NativeJsTypeInterfaceWithStaticInitialization.object) + 1); |
| } |
| |
| static class JavaTypeImplementingNativeJsTypeInterceWithDefaultMethod implements |
| NativeJsTypeInterfaceWithStaticInitializationAndInstanceOverlayMethod { |
| public int getA() { |
| return 4; |
| } |
| } |
| |
| public void testNativeJsTypeWithStaticInitializer() { |
| assertEquals(3, NativeJsTypeInterfaceWithStaticInitializationAndFieldAccess.object); |
| assertEquals( |
| 4, NativeJsTypeInterfaceWithStaticInitializationAndStaticOverlayMethod.getObject()); |
| assertEquals(6, |
| createNativeJsTypeInterfaceWithStaticInitializationAndInstanceOverlayMethod() |
| .getObject()); |
| assertEquals(7, NativeJsTypeInterfaceWithComplexStaticInitialization.object); |
| assertEquals(9, new JavaTypeImplementingNativeJsTypeInterceWithDefaultMethod().getObject()); |
| } |
| |
| @JsFunction |
| interface VarargsFunction { |
| String f(int i, String... args); |
| } |
| |
| private static native String callFromJSNI(VarargsFunction f) /*-{ |
| return f(2, "a", "b", "c"); |
| }-*/; |
| |
| public void testJsVarargsLambda() { |
| VarargsFunction function = (i, args) -> args[i]; |
| assertSame("b", function.f(1, "a", "b", "c")); |
| assertSame("c", callFromJSNI(function)); |
| String[] pars = new String[] {"a", "b", "c"}; |
| assertSame("a", function.f(0, pars)); |
| } |
| |
| private static <T> T m(T s) { |
| return s; |
| } |
| |
| static class Some<T> { |
| T s; |
| MyFunction2<T, T ,T> combine; |
| Some(T s, MyFunction2<T, T, T> combine) { |
| this.s = s; |
| this.combine = combine; |
| } |
| public T m(T s2) { |
| return combine.apply(s, s2); |
| } |
| public T m1() { |
| return s; |
| } |
| } |
| |
| @FunctionalInterface |
| interface MyFunction1<T, U> { |
| U apply(T t); |
| } |
| |
| @FunctionalInterface |
| interface MyFunction2<T, U, V> { |
| V apply(T t, U u); |
| } |
| |
| @FunctionalInterface |
| interface MyFunction3<T, U, V, W> { |
| W apply(T t, U u, V v); |
| } |
| |
| @FunctionalInterface |
| interface IntFunction1<U> { |
| U apply(int t); |
| } |
| |
| @FunctionalInterface |
| interface IntFunction2<V> { |
| V apply(int t, int u); |
| } |
| |
| @FunctionalInterface |
| interface IntFunction3<W> { |
| W apply(int t, int u, int v); |
| } |
| |
| public void testMethodReference_implementedInSuperclass() { |
| MyFunction1<StringBuilder, String> toString = StringBuilder::toString; |
| assertEquals("Hello", toString.apply(new StringBuilder("Hello"))); |
| } |
| |
| static MyFunction2<String, String, String> concat = (s,t) -> s + t; |
| |
| public void testMethodReference_genericTypeParameters() { |
| testMethodReference_genericTypeParameters( |
| new Some<String>("Hell", concat), "Hell", "o", concat); |
| } |
| |
| static String concat(String... strs) { |
| String result = ""; |
| for (String s : strs) { |
| result += s; |
| } |
| return result; |
| } |
| |
| static String anotherConcat(String s1, String s2, String... strs) { |
| String result = s1 + s2; |
| for (String s : strs) { |
| result += s; |
| } |
| return result; |
| } |
| |
| public String instanceConcat(String... strs) { |
| String result = ""; |
| for (String s : strs) { |
| result += s; |
| } |
| return result; |
| } |
| |
| public String anotherInstanceConcat(String s1, String... strs) { |
| String result = s1; |
| for (String s : strs) { |
| result += s; |
| } |
| return result; |
| } |
| |
| private static class ClassWithVarArgsConstructor { |
| private class Inner { |
| private int sum; |
| Inner(int i, Integer... nums) { |
| this.sum = ClassWithVarArgsConstructor.this.sum + i; |
| for (Integer n: nums) { |
| sum += n; |
| } |
| } |
| } |
| |
| private int sum; |
| ClassWithVarArgsConstructor(int i, Integer... nums) { |
| sum = i; |
| for (Integer n: nums) { |
| sum += n; |
| } |
| } |
| |
| private MyFunction1<Integer, Inner> createInner1Param() { |
| return (MyFunction1<Integer, Inner>) Inner::new; |
| } |
| |
| private MyFunction2<Integer, Integer, Inner> createInner2Param() { |
| return (MyFunction2<Integer, Integer, Inner>) Inner::new; |
| } |
| |
| private MyFunction3<Integer, Integer, Integer, Inner> createInner3Param() { |
| return (MyFunction3<Integer, Integer, Integer, Inner>) Inner::new; |
| } |
| |
| private MyFunction2<Integer, Integer[], Inner> createInner2ParamArray() { |
| return (MyFunction2<Integer, Integer[], Inner>) Inner::new; |
| } |
| |
| private IntFunction1<Inner> createInner1IntParam() { |
| return (IntFunction1<Inner>) Inner::new; |
| } |
| |
| private IntFunction2<Inner> createInner2IntParam() { |
| return (IntFunction2<Inner>) Inner::new; |
| } |
| |
| private IntFunction3<Inner> createInner3IntParam() { |
| return (IntFunction3<Inner>) Inner::new; |
| } |
| } |
| |
| public void testMethodReference_varargs() { |
| // More functional arguments than varargs |
| MyFunction2<String, String, String> concat = Java8Test::concat; |
| assertEquals("ab", concat.apply("a", "b")); |
| |
| // Less functional arguments than varargs |
| MyFunction2<String, String, String> anotherConcat = Java8Test::anotherConcat; |
| assertEquals("ab", anotherConcat.apply("a", "b")); |
| |
| MyFunction2<Java8Test, String, String> instanceConcat = Java8Test::instanceConcat; |
| assertEquals("a", instanceConcat.apply(this, "a")); |
| |
| MyFunction2<Java8Test, String, String> anotherInstanceConcat = Java8Test::anotherInstanceConcat; |
| assertEquals("a", anotherInstanceConcat.apply(this, "a")); |
| |
| // constructor varargs |
| MyFunction1<Integer, ClassWithVarArgsConstructor> constructor1Param = |
| ClassWithVarArgsConstructor::new; |
| assertEquals(1, constructor1Param.apply(1).sum); |
| |
| MyFunction2<Integer, Integer, ClassWithVarArgsConstructor> constructor2Param = |
| ClassWithVarArgsConstructor::new; |
| assertEquals(3, constructor2Param.apply(1, 2).sum); |
| |
| MyFunction3<Integer, Integer, Integer, ClassWithVarArgsConstructor> constructor3Param = |
| ClassWithVarArgsConstructor::new; |
| assertEquals(6, constructor3Param.apply(1, 2, 3).sum); |
| |
| MyFunction2<Integer, Integer[], ClassWithVarArgsConstructor> constructor2ParamArray = |
| ClassWithVarArgsConstructor::new; |
| assertEquals(6, constructor2ParamArray.apply(1, new Integer[] {2, 3}).sum); |
| |
| // constructor varargs + autoboxing |
| IntFunction1<ClassWithVarArgsConstructor> constructor1IntParam = |
| ClassWithVarArgsConstructor::new; |
| assertEquals(1, constructor1IntParam.apply(1).sum); |
| |
| IntFunction2<ClassWithVarArgsConstructor> constructor2IntParam = |
| ClassWithVarArgsConstructor::new; |
| assertEquals(3, constructor2IntParam.apply(1, 2).sum); |
| |
| IntFunction3<ClassWithVarArgsConstructor> constructor3IntParam = |
| ClassWithVarArgsConstructor::new; |
| assertEquals(6, constructor3IntParam.apply(1, 2, 3).sum); |
| |
| ClassWithVarArgsConstructor outer = new ClassWithVarArgsConstructor(1); |
| |
| // inner class constructor varargs |
| assertEquals(2, outer.createInner1Param().apply(1).sum); |
| assertEquals(4, outer.createInner2Param().apply(1, 2).sum); |
| assertEquals(7, outer.createInner3Param().apply(1, 2, 3).sum); |
| assertEquals(7, outer.createInner2ParamArray().apply(1, new Integer[] {2, 3}).sum); |
| |
| // inner class constructor varargs + autoboxing |
| assertEquals(2, outer.createInner1IntParam().apply(1).sum); |
| assertEquals(4, outer.createInner2IntParam().apply(1, 2).sum); |
| assertEquals(7, outer.createInner3IntParam().apply(1, 2, 3).sum); |
| } |
| |
| private static <T> void testMethodReference_genericTypeParameters( |
| Some<T> some, T t1, T t2, MyFunction2<T, T, T> combine) { |
| T t1t2 = combine.apply(t1, t2); |
| |
| // Test all 4 flavours of methodReference |
| // 1. Static method |
| assertEquals(t1t2, ((MyFunction1<T, T>) Java8Test::m).apply(t1t2)); |
| // 2. Qualified instance method |
| assertEquals(t1t2, ((MyFunction1<T, T>) some::m).apply(t2)); |
| // 3. Unqualified instance method |
| assertEquals(t1, ((MyFunction1<Some<T>, T>) Some<T>::m1).apply(some)); |
| assertEquals("Hello", |
| ((MyFunction1<Some<String>, String>) |
| Some<String>::m1).apply(new Some<>("Hello", concat))); |
| // 4. Constructor reference. |
| assertEquals(t1t2, |
| ((MyFunction2<T, MyFunction2<T, T, T>, Some<T>>) Some<T>::new).apply(t1t2, combine).m1()); |
| } |
| |
| static MyFunction2<Integer, Integer, Integer> addInteger = (s,t) -> s + t; |
| |
| @FunctionalInterface |
| interface MyIntFunction1 { |
| int apply(int t); |
| } |
| |
| @FunctionalInterface |
| interface MyIntFunction2 { |
| int apply(int t, int u); |
| } |
| |
| @FunctionalInterface |
| interface MyIntFuncToSomeIntegeFunction2 { |
| SomeInteger apply(int t, MyFunction2<Integer, Integer, Integer> u); |
| } |
| |
| @FunctionalInterface |
| interface MySomeIntegerFunction1 { |
| int apply(SomeInteger t); |
| } |
| |
| @FunctionalInterface |
| interface MySomeIntegerIntFunction2 { |
| int apply(SomeInteger t, int u); |
| } |
| |
| static MyIntFunction2 addint = (s,t) -> s + t; |
| |
| static class SomeInteger { |
| int s; |
| MyFunction2<Integer, Integer ,Integer> combine; |
| SomeInteger(int s, MyFunction2<Integer, Integer, Integer> combine) { |
| this.s = s; |
| this.combine = combine; |
| } |
| public int m(int s2) { |
| return combine.apply(s, s2); |
| } |
| public int m1() { |
| return s; |
| } |
| } |
| |
| public void testMethodReference_autoboxing() { |
| SomeInteger some = new SomeInteger(3, addInteger); |
| |
| // Test all 4 flavours of methodReference autoboxing parameters. |
| // 1. Static method |
| assertEquals((Integer) 5, ((MyFunction1<Integer, Integer>) Java8Test::m).apply(5)); |
| // 2. Qualified instance method |
| assertEquals((Integer) 5, ((MyFunction1<Integer, Integer>) some::m).apply(2)); |
| // 3. Unqualified instance method |
| assertEquals((Integer) 3, ((MyFunction1<SomeInteger, Integer>) SomeInteger::m1).apply(some)); |
| assertEquals((Integer) 5, ((MyFunction2<SomeInteger, Integer, Integer>) |
| SomeInteger::m).apply(some, 2)); |
| assertEquals((Integer) 5, |
| ((MyFunction1<SomeInteger, Integer>) |
| SomeInteger::m1).apply(new SomeInteger(5, addInteger))); |
| // 4. Constructor reference. |
| assertEquals(5, |
| ((MyFunction2<Integer, MyFunction2<Integer, Integer, Integer>, SomeInteger>) |
| SomeInteger::new).apply(5, addInteger).m1()); |
| |
| // Test all 4 flavours of methodReference (interface unboxed) |
| // 1. Static method |
| assertEquals(5, ((MyIntFunction1) Java8Test::m).apply(5)); |
| // 2. Qualified instance method |
| assertEquals(5, ((MyIntFunction1) some::m).apply(2)); |
| // 3. Unqualified instance method |
| assertEquals(3, ((MySomeIntegerFunction1) SomeInteger::m1).apply(some)); |
| // The next expression was the one that triggered bug #9346 where decisions on whether to |
| // box/unbox were decided incorrectly due to differring number of parameters in the method |
| // reference and the functional interface method. |
| assertEquals(5, ((MySomeIntegerIntFunction2) SomeInteger::m).apply(some, 2)); |
| assertEquals(5, |
| ((MySomeIntegerFunction1) |
| SomeInteger::m1).apply(new SomeInteger(5, addInteger))); |
| // 4. Constructor reference. |
| assertEquals(5, |
| ((MyIntFuncToSomeIntegeFunction2) SomeInteger::new).apply(5, addInteger).m1()); |
| } |
| |
| @JsType(isNative = true) |
| private static class NativeClassWithJsOverlay { |
| @JsOverlay |
| public static String m(String s) { |
| MyFunction1<String, String> id = (a) -> a; |
| return id.apply(s); |
| } |
| } |
| public void testNativeJsOverlay_lambda() { |
| assertSame("Hello", NativeClassWithJsOverlay.m("Hello")); |
| } |
| |
| interface IntefaceWithDefaultMethodAndLambda { |
| boolean f(); |
| |
| default BooleanPredicate fAsPredicate() { |
| // This lambda will be defined as an instance method in the enclosing class, which is an |
| // interface. In this case the methdod will be devirtualized. |
| return () -> this.f(); |
| } |
| } |
| |
| interface BooleanPredicate { |
| boolean apply(); |
| } |
| |
| public void testLambdaCapturingThis_onDefaultMethod() { |
| assertTrue( |
| new IntefaceWithDefaultMethodAndLambda() { |
| @Override |
| public boolean f() { |
| return true; |
| } |
| }.fAsPredicate().apply()); |
| } |
| |
| @JsFunction |
| interface MyJsFunctionInterface { |
| int foo(int a); |
| } |
| |
| public void testJsFunction_lambda() { |
| MyJsFunctionInterface jsFunctionInterface = a -> a + 2; |
| assertEquals(12, callAsFunction(jsFunctionInterface, 10)); |
| assertEquals(12, jsFunctionInterface.foo(10)); |
| } |
| |
| private static native int callAsFunction(Object fn, int arg) /*-{ |
| return fn(arg); |
| }-*/; |
| |
| @JsFunction |
| interface MyJsFunctionInterfaceWithOverlay { |
| Double m(); |
| @JsOverlay |
| default Double callM() { |
| return this.m(); |
| } |
| } |
| |
| private static native MyJsFunctionInterfaceWithOverlay createNative() /*-{ |
| return function () { return 5; }; |
| }-*/; |
| |
| public void testJsFunction_withOverlay() { |
| MyJsFunctionInterfaceWithOverlay f = new MyJsFunctionInterfaceWithOverlay() { |
| @Override |
| public Double m() { |
| return new Double(2.0); |
| } |
| }; |
| assertEquals(2, f.callM().intValue()); |
| assertEquals(5, createNative().callM().intValue()); |
| } |
| |
| interface FunctionalExpressionBridges_I<T> { |
| T apply(T t); |
| // TODO(rluble): uncomment the line below to when bridges for default methods are created |
| // in functional expressions |
| FunctionalExpressionBridges_I<T> m(T t); |
| } |
| |
| @FunctionalInterface |
| interface FunctionalExpressionBridges_J<T extends Comparable> |
| extends FunctionalExpressionBridges_I<T> { |
| T apply(T t); |
| |
| // Overrides I.m() and specializes return type |
| default FunctionalExpressionBridges_J<T> m(T t) { |
| return this; |
| } |
| } |
| |
| public static String identity(String s) { |
| return s; |
| } |
| |
| public void testFunctionalExpressionBridges() { |
| FunctionalExpressionBridges_J<String> ann = new FunctionalExpressionBridges_J<String>() { |
| @Override |
| public String apply(String string) { |
| return string; |
| } |
| }; |
| |
| assertBrigdeDispatchIsCorrect(ann); |
| assertBrigdeDispatchIsCorrect((String s) -> s + ""); |
| assertBrigdeDispatchIsCorrect(Java8Test::identity); |
| } |
| |
| private void assertBrigdeDispatchIsCorrect( |
| FunctionalExpressionBridges_J<String> functionalExpression) { |
| assertEquals("Hello", functionalExpression.m(null).apply("Hello")); |
| assertEquals("Hello", functionalExpression.apply("Hello")); |
| assertEquals("Hello", |
| ((FunctionalExpressionBridges_I<String>) functionalExpression).apply("Hello")); |
| } |
| |
| static class ClassWithAVeryLoooooooooooooooooooooooooooooooooooongName { |
| public static String m() { |
| return null; |
| } |
| } |
| |
| // Regression test for bug: #9426. |
| public void testCorrectNaming() { |
| Function<String> f = ClassWithAVeryLoooooooooooooooooooooooooooooooooooongName::m; |
| assertNotNull(f); |
| } |
| |
| @JsType(isNative = true) |
| interface InterfaceWithOverlay { |
| |
| @JsProperty |
| int getLength(); |
| |
| @JsOverlay |
| default int len() { |
| return this.getLength(); |
| } |
| } |
| |
| @JsType(isNative = true, name = "Object", namespace = JsPackage.GLOBAL) |
| static abstract class SubclassImplementingInterfaceWithOverlay implements InterfaceWithOverlay { |
| } |
| |
| // Regression test for bug: #9440 |
| public void testInterfaceWithOverlayAndNativeSubclass() { |
| SubclassImplementingInterfaceWithOverlay object = |
| (SubclassImplementingInterfaceWithOverlay) (Object) new int[]{1, 2, 3}; |
| assertEquals(3, object.len()); |
| } |
| |
| interface Producer<T> { |
| T get(); |
| } |
| |
| private static Producer<Object> createInnerClassProducer() { |
| class InnerClass { |
| } |
| return (Producer) InnerClass::new; |
| } |
| |
| public void testLocalClassConstructorReferenceInStaticMethod() { |
| assertTrue(createInnerClassProducer().get() != null); |
| } |
| |
| // NOTE: DO NOT reorder the following classes, bug #9453 is only reproducible in certain |
| // orderings. |
| interface SubSub_SuperDefaultMethodDevirtualizationOrder |
| extends Sub_SuperDefaultMethodDevirtualizationOrder { |
| default String m() { |
| return Sub_SuperDefaultMethodDevirtualizationOrder.super.m(); |
| } |
| } |
| |
| interface Sub_SuperDefaultMethodDevirtualizationOrder |
| extends Super_SuperDefaultMethodDevirtualizationOrder { |
| @Override |
| default String m() { |
| return Super_SuperDefaultMethodDevirtualizationOrder.super.m(); |
| } |
| } |
| |
| interface Super_SuperDefaultMethodDevirtualizationOrder { |
| default String m() { |
| return "Hi"; |
| } |
| } |
| |
| // Regression test for bug #9453. |
| public void testDefaultMethodDevirtualizationOrder() { |
| assertEquals("Hi", new SubSub_SuperDefaultMethodDevirtualizationOrder() { |
| }.m()); |
| } |
| |
| private static String first(String... strings) { |
| return strings[0]; |
| } |
| |
| // Regresion test for https://github.com/gwtproject/gwt/issues/9497 |
| public void testVarargsFunctionalConversion() { |
| java.util.function.Function<String[], String> function = Java8Test::first; |
| assertEquals("Hello", function.apply(new String[] {"Hello", "GoodBye"})); |
| } |
| |
| interface SingleJsoImplA { |
| String getAData(); |
| |
| List<SingleJsoImplB> getListOfB(); |
| } |
| |
| interface SingleJsoImplB { |
| String getBData(); |
| } |
| |
| private static final class AOverlay extends JavaScriptObject implements SingleJsoImplA { |
| protected AOverlay() { } |
| |
| @Override |
| public native String getAData() /*-{ |
| return this.data; |
| }-*/; |
| |
| @Override |
| public native List<SingleJsoImplB> getListOfB() /*-{ |
| return @java.util.Arrays::asList(*)(this.listOfb); |
| }-*/; |
| } |
| |
| private static final class BOverlay extends JavaScriptObject implements SingleJsoImplB { |
| protected BOverlay() { } |
| |
| @Override |
| public native String getBData() /*-{ |
| return this.data; |
| }-*/; |
| } |
| |
| private static SingleJsoImplA createA() { |
| return JsonUtils.safeEval( |
| "{\"data\":\"a value\",\"listOfb\":[{\"data\":\"b1\"},{\"data\":\"b2\"}]}"); |
| } |
| |
| // Regression for issue #9558 |
| public void testJSOLivenessSingleImplErasure() { |
| SingleJsoImplA a = createA(); |
| String result = a.getListOfB().stream() |
| .map(SingleJsoImplB::getBData).collect(Collectors.joining(",")); |
| assertEquals("b1,b2", result); |
| result = a.getListOfB().stream() |
| .map(b -> b.getBData()).collect(Collectors.joining(",")); |
| assertEquals("b1,b2", result); |
| } |
| |
| @SuppressWarnings({"rawtypes", "unchecked"}) |
| public void testLambdaErasureCasts() { |
| List list = new ArrayList<String>(); |
| list.add("2"); |
| try { |
| ((List<Integer>) list).stream().map(n -> n.intValue() == 2).findAny(); |
| fail("Should have thrown."); |
| } catch (ClassCastException expected) { |
| } |
| } |
| |
| public void testLambdaBoxing() { |
| BiFunction<Integer, Integer, Boolean> equals = (i, j) -> i + 0 == j; |
| assertTrue(equals.apply(1,1)); |
| assertTrue(equals.apply(new Integer(2),2)); |
| assertTrue(equals.apply(new Integer(3), new Integer(3))); |
| |
| IntFunction<Integer> unboxBox = i -> i; |
| assertEquals(2, (int) unboxBox.apply(2)); |
| assertEquals(2, (int) unboxBox.apply(new Integer(2))); |
| } |
| |
| //////////////////////////////////////////////////////////// |
| // |
| // Tests for language features introduced in Java 9 |
| |
| class Resource implements AutoCloseable { |
| boolean isOpen = true; |
| |
| public void close() { |
| this.isOpen = false; |
| } |
| } |
| |
| public void testTryWithResourcesJava9() { |
| Resource r1 = new Resource(); |
| assertTrue(r1.isOpen); |
| Resource r2Copy; |
| try (r1; Resource r2 = new Resource()) { |
| assertTrue(r1.isOpen); |
| assertTrue(r2.isOpen); |
| r2Copy = r2; |
| } |
| assertFalse(r1.isOpen); |
| assertFalse(r2Copy.isOpen); |
| } |
| |
| private interface InterfaceWithPrivateMethods { |
| int implementedMethod(); |
| |
| default int defaultMethod() { |
| return privateMethod(); |
| } |
| |
| private int privateMethod() { |
| return implementedMethod(); |
| } |
| |
| private int staticPrivateMethod() { |
| return 42; |
| } |
| } |
| |
| public void testInterfacePrivateMethodsJava9() { |
| InterfaceWithPrivateMethods implementor = () -> 50; |
| assertEquals(50, implementor.implementedMethod()); |
| assertEquals(50, implementor.defaultMethod()); |
| assertEquals(42, implementor.staticPrivateMethod()); |
| } |
| |
| public void testAnonymousDiamondJava9() { |
| Supplier<String> helloSupplier = new Supplier<>() { |
| @Override |
| public String get() { |
| return "hello"; |
| } |
| }; |
| assertEquals("hello", helloSupplier.get()); |
| } |
| |
| interface Selector extends Predicate<String> { |
| @Override |
| boolean test(String object); |
| |
| default Selector trueSelector() { |
| // Unused variable that creates a lambda with a bridge for the method test. The bug #9598 |
| // was caused by GwtAstBuilder associating the bridge method Lambda.test(Object) on the |
| // lambda below to the method Predicate.test(Object), causing the method resolution in the |
| // code that refers to the Predicate.test(Object) in the test below to refer to |
| // Lambda.test(Object) which is the wrong method. |
| return receiver -> true; |
| } |
| } |
| |
| // Regression tests for #9598 |
| public void testImproperMethodResolution() { |
| Predicate p = o -> true; |
| assertTrue(p.test(null)); |
| } |
| |
| interface I2<T> { public T foo(T arg); } |
| |
| interface I1 extends I2<String> { public String foo(String arg0); } |
| |
| @SuppressWarnings({"rawtypes", "unchecked"}) |
| public void testIntersectionCastLambda() { |
| |
| Object instance = (I1 & I2<String>) val -> "#" + val; |
| |
| assertTrue(instance instanceof I1); |
| assertTrue(instance instanceof I2); |
| |
| I1 lambda = (I1) instance; |
| I2 raw = lambda; |
| assertEquals("#1", raw.foo("1")); // tests that the bridge exists and is correct |
| assertEquals("#2", lambda.foo("2")); |
| } |
| |
| static class C { public static String append(String str) { return "#" + str; } } |
| @SuppressWarnings({"rawtypes", "unchecked"}) |
| public void testIntersectionCastMethodReference() { |
| |
| Object instance = (I1 & I2<String>) C::append; |
| |
| assertTrue(instance instanceof I1); |
| assertTrue(instance instanceof I2); |
| |
| I1 lambda = (I1) instance; |
| I2 raw = lambda; |
| assertEquals("#1", raw.foo("1")); // tests that the bridge exists and is correct |
| assertEquals("#2", lambda.foo("2")); |
| } |
| } |