net.orfjackal.retrolambda.test.LambdaTest.java Source code

Java tutorial

Introduction

Here is the source code for net.orfjackal.retrolambda.test.LambdaTest.java

Source

// Copyright  2013-2016 Esko Luontola <www.orfjackal.net>
// This software is released under the Apache License 2.0.
// The license text is at http://www.apache.org/licenses/LICENSE-2.0

package net.orfjackal.retrolambda.test;

import net.orfjackal.retrolambda.test.anotherpackage.DifferentPackageBase;
import org.apache.commons.lang.SystemUtils;
import org.junit.Test;
import org.objectweb.asm.*;
import org.objectweb.asm.Type;

import java.io.IOException;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.Callable;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeThat;

public class LambdaTest extends SuperClass {

    @Test
    public void empty_lambda() {
        Runnable lambda = () -> {
        };

        lambda.run();
    }

    @Test
    public void lambda_returning_a_value() throws Exception {
        Callable<String> lambda = () -> "some value";

        assertThat(lambda.call(), is("some value"));
    }

    private interface Function1<IN, OUT> {
        OUT apply(IN value);
    }

    @Test
    public void lambda_taking_parameters() {
        Function1<String, Integer> lambda = (String s) -> s.getBytes().length;

        assertThat(lambda.apply("foo"), is(3));
    }

    @Test
    public void lambda_in_project_main_sources() throws Exception {
        assertThat(InMainSources.callLambda(), is(42));
    }

    private int instanceVar = 0;

    @Test
    public void lambda_using_instance_variables() {
        Runnable lambda = () -> {
            instanceVar = 42;
        };
        lambda.run();

        assertThat(instanceVar, is(42));
    }

    @Test
    public void lambda_using_local_variables() {
        int[] localVar = new int[1];
        Runnable lambda = () -> {
            localVar[0] = 42;
        };
        lambda.run();

        assertThat(localVar[0], is(42));
    }

    @Test
    public void lambda_using_local_variables_of_primitive_types() throws Exception {
        boolean bool = true;
        byte b = 2;
        short s = 3;
        int i = 4;
        long l = 5;
        float f = 6;
        double d = 7;
        char c = 8;
        Callable<Integer> lambda = () -> (int) ((bool ? 1 : 0) + b + s + i + l + f + d + c);

        assertThat(lambda.call(), is(36));
    }

    @Test
    public void lambda_in_the_constant_initializer_of_an_interface() throws Exception {
        assertThat(LambdaConstant.LAMBDA.call(), is("foo"));
    }

    public interface LambdaConstant {
        Callable<String> LAMBDA = () -> "foo";
    }

    @Test
    public void lambdas_with_marker_interfaces_due_to_intersection_types() throws Exception {
        // We must use something other than java.io.Serializable as the marker interface,
        // because serializable lambdas are signified by a flag to LambdaMetafactory.altMetafactory
        Callable<String> lambda = (Callable<String> & Cloneable) () -> "foo";

        assertThat(lambda, is(instanceOf(Cloneable.class)));
        assertThat(lambda.call(), is("foo"));
    }

    @Test
    public void method_references_to_virtual_methods_on_local_variables() throws Exception {
        String foo = "foo";
        Callable<String> ref = foo::toUpperCase;

        assertThat(ref.call(), is("FOO"));
    }

    @Test
    public void method_references_to_virtual_methods_on_instance_variables() throws Exception {
        Callable<String> ref = instanceVarFoo::toUpperCase;

        assertThat(ref.call(), is("FOO"));
    }

    @SuppressWarnings("FieldCanBeLocal")
    private String instanceVarFoo = "foo";

    @Test
    public void method_references_to_interface_methods() throws Exception {
        List<String> foos = Arrays.asList("foo");
        Callable<Integer> ref = foos::size;

        assertThat(ref.call(), is(1));
    }

    @Test
    public void method_references_to_static_methods() throws Exception {
        long expected = System.currentTimeMillis();
        Callable<Long> ref = System::currentTimeMillis;

        assertThat(ref.call(), is(greaterThanOrEqualTo(expected)));
    }

    @Test
    public void method_references_to_constructors() throws Exception {
        Callable<List<String>> ref = ArrayList<String>::new;

        assertThat(ref.call(), is(instanceOf(ArrayList.class)));
    }

    @Test
    public void method_references_to_protected_supertype_methods() throws Exception {
        Callable<String> ref1 = new SubclassInMyPackage().thing();
        assertThat(ref1.call(), equalTo("Hello"));

        Callable<String> ref2 = new SubclassInSamePackage().thing();
        assertThat(ref2.call(), equalTo("Hello"));
    }

    public static class SubclassInMyPackage extends DifferentPackageBase {
        public Callable<String> thing() {
            return DifferentPackageBase::value;
        }
    }

    public static class SubclassInSamePackage extends SamePackageBase {
        public Callable<String> thing() {
            return SamePackageBase::value;
        }
    }

    /**
     * Because the constructor is private, an access method must be generated for it
     * and also the NEW instruction must be done inside the access method.
     */
    @Test
    public void method_references_to_private_constructors() throws Exception {
        Callable<HasPrivateConstructor> factory = HasPrivateConstructor.factory();
        assertThat(factory.call(), is(instanceOf(HasPrivateConstructor.class)));

        HasPrivateConstructorWithArgs.Factory factoryArgs = HasPrivateConstructorWithArgs.factory();
        assertThat(factoryArgs.create("args"), is(instanceOf(HasPrivateConstructorWithArgs.class)));
        assertThat(factoryArgs.create("args").args, is("args"));
    }

    public static class HasPrivateConstructor {

        private HasPrivateConstructor() {
        }

        public static Callable<HasPrivateConstructor> factory() {
            return HasPrivateConstructor::new;
        }
    }

    public static class HasPrivateConstructorWithArgs {
        public final String args;

        private HasPrivateConstructorWithArgs(String args) {
            this.args = args;
        }

        public static Factory factory() {
            return HasPrivateConstructorWithArgs::new;
        }

        public interface Factory {
            HasPrivateConstructorWithArgs create(String args);
        }
    }

    @Test
    public void method_references_to_overridden_inherited_methods_with_super() throws Exception {
        Callable<String> ref = super::inheritedMethod;

        assertThat(ref.call(), is("superclass version"));
    }

    @Override
    String inheritedMethod() {
        return "overridden version";
    }

    @Test
    public void method_references_to_private_methods() throws Exception {
        Callable<String> ref1 = LambdaTest::privateClassMethod;
        assertThat(ref1.call(), is("foo"));

        Callable<String> ref2 = this::privateInstanceMethod;
        assertThat(ref2.call(), is("foo"));

        // Normal method calls should still work after our magic
        // of making them them accessible from the lambda classes.
        assertThat(privateClassMethod(), is("foo"));
        assertThat(privateInstanceMethod(), is("foo"));
    }

    private String privateInstanceMethod() {
        return "foo";
    }

    private static String privateClassMethod() {
        return "foo";
    }

    /**
     * We could make private lambda implementation methods package-private,
     * so that the lambda class may call them, but we should not make any
     * more methods non-private than is absolutely necessary.
     */
    @Test
    public void will_not_change_the_visibility_of_unrelated_methods() throws Exception {
        assertThat(unrelatedPrivateMethod(), is("foo"));

        Method method = getClass().getDeclaredMethod("unrelatedPrivateMethod");
        int modifiers = method.getModifiers();

        assertTrue("expected " + method.getName() + " to be private, but modifiers were: "
                + Modifier.toString(modifiers), Modifier.isPrivate(modifiers));
    }

    private String unrelatedPrivateMethod() {
        return "foo";
    }

    /**
     * We cannot just make the private methods package-private for the
     * lambda class to call them, because that may cause a method in subclass
     * to override them.
     */
    @Test
    public void will_not_cause_private_methods_to_be_overridable() throws Exception {
        class Parent {
            private String privateMethod() {
                return "parent version";
            }

            Callable<String> parentRef() {
                return this::privateMethod;
            }
        }
        class Child extends Parent {
            private String privateMethod() { // would override if were not private
                return "child version";
            }

            Callable<String> childRef() {
                return this::privateMethod;
            }
        }

        Child child = new Child();

        // Our test assumes that there exists a private method with
        // the same name and signature in super and sub classes.
        String name = "privateMethod";
        assertThat(child.getClass().getDeclaredMethod(name), is(notNullValue()));
        assertThat(child.getClass().getSuperclass().getDeclaredMethod(name), is(notNullValue()));

        assertThat(child.privateMethod(), is("child version"));
        assertThat(child.childRef().call(), is("child version"));

        assertThat(((Parent) child).privateMethod(), is("parent version"));
        assertThat(child.parentRef().call(), is("parent version"));
    }

    @Test
    public void bytecode_constant_pool_will_not_contain_dangling_references_to_MethodHandles() throws IOException {
        assumeThat(SystemUtils.JAVA_VERSION_FLOAT, is(lessThan(1.7f)));

        ClassReader cr = new ClassReader(getClass().getName().replace('.', '/'));
        char[] buf = new char[cr.getMaxStringLength()];

        for (int item = 0; item < cr.getItemCount(); item++) {
            Object constant = readConstant(item, buf, cr);
            if (constant instanceof Type) {
                Type type = (Type) constant;
                assertThat("constant #" + item, type.getDescriptor(), not(containsString("java/lang/invoke")));
            }
        }
    }

    private static Object readConstant(int item, char[] buf, ClassReader cr) {
        try {
            return cr.readConst(item, buf);
        } catch (Exception e) {
            // XXX: constant pool entry which is a Methodref, InvokeDynamic or similar non-plain constant
            return null;
        }
    }
}

class SuperClass {
    String inheritedMethod() {
        return "superclass version";
    }
}