diff --git a/java-lambda/README.md b/java-lambda/README.md
new file mode 100644
index 0000000000..8b3159ddcd
--- /dev/null
+++ b/java-lambda/README.md
@@ -0,0 +1,35 @@
+Cucumber Java8
+==============
+
+Provides lambda based step definitions. To use add the `cucumber-java-lambda` dependency to your pom.xml:
+
+```xml
+
+ [...]
+
+ io.cucumber
+ cucumber-java-lambda
+ ${cucumber.version}
+ test
+
+ [...]
+
+```
+
+## Step Definitions
+
+## Hooks
+
+## Transformers
+
+### Parameter Type
+
+### Data Table Type
+
+#### Default Transformers
+
+#### Empty Cells
+
+#### Transposing Tables
+
+### DocString type
diff --git a/java-lambda/pom.xml b/java-lambda/pom.xml
new file mode 100644
index 0000000000..dca27604d6
--- /dev/null
+++ b/java-lambda/pom.xml
@@ -0,0 +1,80 @@
+
+ 4.0.0
+
+
+ io.cucumber
+ cucumber-jvm
+ 7.3.3-SNAPSHOT
+
+
+ cucumber-java-lambda
+ jar
+ Cucumber-JVM: Java Lambda
+
+
+ io.cucumber.lambda
+ 1.1.2
+ 5.8.2
+ 0.6.3
+ 3.22.0
+
+
+
+
+
+ io.cucumber
+ cucumber-bom
+ ${project.version}
+ pom
+ import
+
+
+ org.junit
+ junit-bom
+ ${junit-jupiter.version}
+ pom
+ import
+
+
+
+
+
+
+ io.cucumber
+ cucumber-core
+
+
+ org.apiguardian
+ apiguardian-api
+ ${apiguardian-api.version}
+
+
+ net.jodah
+ typetools
+ ${typetools.version}
+
+
+
+ io.cucumber
+ cucumber-junit-platform-engine
+ test
+
+
+ org.junit.platform
+ junit-platform-suite
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+
+ org.assertj
+ assertj-core
+ ${assertj.version}
+
+
+
+
diff --git a/java-lambda/src/main/java/io/cucumber/lambda/Invoker.java b/java-lambda/src/main/java/io/cucumber/lambda/Invoker.java
new file mode 100644
index 0000000000..f7da075e98
--- /dev/null
+++ b/java-lambda/src/main/java/io/cucumber/lambda/Invoker.java
@@ -0,0 +1,30 @@
+package io.cucumber.lambda;
+
+import io.cucumber.core.backend.CucumberBackendException;
+import io.cucumber.core.backend.CucumberInvocationTargetException;
+import io.cucumber.core.backend.Located;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+final class Invoker {
+
+ private Invoker() {
+
+ }
+
+ static Object invoke(Located located, Object target, Method method, Object... args) {
+ boolean accessible = method.canAccess(target);
+ try {
+ method.setAccessible(true);
+ return method.invoke(target, args);
+ } catch (IllegalArgumentException | IllegalAccessException e) {
+ throw new CucumberBackendException("Failed to invoke " + located.getLocation(), e);
+ } catch (InvocationTargetException e) {
+ throw new CucumberInvocationTargetException(located, e);
+ } finally {
+ method.setAccessible(accessible);
+ }
+ }
+
+}
diff --git a/java-lambda/src/main/java/io/cucumber/lambda/StepDeclaration.java b/java-lambda/src/main/java/io/cucumber/lambda/StepDeclaration.java
new file mode 100644
index 0000000000..8baeb54096
--- /dev/null
+++ b/java-lambda/src/main/java/io/cucumber/lambda/StepDeclaration.java
@@ -0,0 +1,77 @@
+package io.cucumber.lambda;
+
+import io.cucumber.core.backend.Located;
+import io.cucumber.core.backend.SourceReference;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import static io.cucumber.core.backend.SourceReference.fromStackTraceElement;
+import static java.lang.String.format;
+import static java.util.Objects.requireNonNull;
+
+class StepDeclaration implements Located {
+ private final String expression;
+ private final Class> context;
+ private final StepDefinitionFunction function;
+ private final StackTraceElement location;
+ private SourceReference sourceReference;
+
+ StepDeclaration(String expression, Class> context, StepDefinitionFunction function, StackTraceElement location) {
+ this.expression = requireNonNull(expression, "expression should not be null");
+ this.context = requireNonNull(context, "context should not be null");
+ this.function = requireNonNull(function, "function should not be null");
+ this.location = requireNonNull(location, "location should not be null");
+
+ }
+
+ String getExpression() {
+ return expression;
+ }
+
+ Class> getContext() {
+ return context;
+ }
+
+ @Override
+ public final boolean isDefinedAt(StackTraceElement stackTraceElement) {
+ return location.getFileName() != null && location.getFileName().equals(stackTraceElement.getFileName());
+ }
+
+ @Override
+ public Optional getSourceReference() {
+ if (sourceReference == null) {
+ sourceReference = fromStackTraceElement(location);
+ }
+ return Optional.of(sourceReference);
+ }
+
+ @Override
+ public final String getLocation() {
+ return location.toString();
+ }
+
+ void invoke(Object contextInstance, Object[] arguments) {
+ Method acceptMethod = getAcceptMethod(function.getClass());
+ Object body = Invoker.invoke(this, function, acceptMethod, contextInstance);
+ Method bodyAcceptMethod = getAcceptMethod(body.getClass());
+ Invoker.invoke(this, body, bodyAcceptMethod, arguments);
+ }
+
+ private Method getAcceptMethod(Class> bodyClass) {
+ List acceptMethods = new ArrayList<>();
+ for (Method method : bodyClass.getDeclaredMethods()) {
+ if (!method.isBridge() && !method.isSynthetic() && "accept".equals(method.getName())) {
+ acceptMethods.add(method);
+ }
+ }
+ if (acceptMethods.size() != 1) {
+ throw new IllegalStateException(format(
+ "Expected single 'accept' method on body class, found '%s'", acceptMethods));
+ }
+ return acceptMethods.get(0);
+ }
+
+}
diff --git a/java-lambda/src/main/java/io/cucumber/lambda/StepDefinitionFunction.java b/java-lambda/src/main/java/io/cucumber/lambda/StepDefinitionFunction.java
new file mode 100644
index 0000000000..dcc1e44250
--- /dev/null
+++ b/java-lambda/src/main/java/io/cucumber/lambda/StepDefinitionFunction.java
@@ -0,0 +1,140 @@
+package io.cucumber.lambda;
+
+import org.apiguardian.api.API;
+
+import static org.apiguardian.api.API.Status.EXPERIMENTAL;
+
+@API(status = EXPERIMENTAL, since = "7.4.0")
+public interface StepDefinitionFunction {
+
+ @FunctionalInterface
+ interface C0A0 extends StepDefinitionFunction {
+ StepDefinitionBody.A0 accept();
+
+ }
+
+ @FunctionalInterface
+ interface C1A0 extends StepDefinitionFunction {
+ StepDefinitionBody.A0 accept(Context context);
+
+ }
+
+ @FunctionalInterface
+ interface C1A1 extends StepDefinitionFunction {
+ StepDefinitionBody.A1 accept(Context context);
+
+ }
+
+ @FunctionalInterface
+ interface C1A2 extends StepDefinitionFunction {
+ StepDefinitionBody.A2 accept(Context context);
+
+ }
+
+ @FunctionalInterface
+ interface C1A3 extends StepDefinitionFunction {
+ StepDefinitionBody.A3 accept(Context context);
+
+ }
+
+ @FunctionalInterface
+ interface C1A4 extends StepDefinitionFunction {
+ StepDefinitionBody.A4 accept(Context context);
+
+ }
+
+ @FunctionalInterface
+ interface C1A5 extends StepDefinitionFunction {
+ StepDefinitionBody.A5 accept(Context context);
+
+ }
+
+ @FunctionalInterface
+ interface C1A6 extends StepDefinitionFunction {
+ StepDefinitionBody.A6 accept(Context context);
+
+ }
+
+ @FunctionalInterface
+ interface C1A7 extends StepDefinitionFunction {
+ StepDefinitionBody.A7 accept(Context context);
+
+ }
+
+ @FunctionalInterface
+ interface C1A8 extends StepDefinitionFunction {
+ StepDefinitionBody.A8 accept(Context context);
+
+ }
+
+ @FunctionalInterface
+ interface C1A9 extends StepDefinitionFunction {
+ StepDefinitionBody.A9 accept(Context context);
+
+ }
+
+ interface StepDefinitionBody {
+
+ @FunctionalInterface
+ interface A0 extends StepDefinitionBody {
+ void accept() throws Throwable;
+
+ }
+
+ @FunctionalInterface
+ interface A1 extends StepDefinitionBody {
+ void accept(T1 t1) throws Throwable;
+
+ }
+
+ @FunctionalInterface
+ interface A2 extends StepDefinitionBody {
+ void accept(T1 t1, T2 t2) throws Throwable;
+
+ }
+
+ @FunctionalInterface
+ interface A3 extends StepDefinitionBody {
+ void accept(T1 t1, T2 t2, T3 t3) throws Throwable;
+
+ }
+
+ @FunctionalInterface
+ interface A4 extends StepDefinitionBody {
+ void accept(T1 t1, T2 t2, T3 t3, T4 t4) throws Throwable;
+
+ }
+
+ @FunctionalInterface
+ interface A5 extends StepDefinitionBody {
+ void accept(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5) throws Throwable;
+
+ }
+
+ @FunctionalInterface
+ interface A6 extends StepDefinitionBody {
+ void accept(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6) throws Throwable;
+
+ }
+
+ @FunctionalInterface
+ interface A7 extends StepDefinitionBody {
+ void accept(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7) throws Throwable;
+
+ }
+
+ @FunctionalInterface
+ interface A8 extends StepDefinitionBody {
+ void accept(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8) throws Throwable;
+
+ }
+
+ @FunctionalInterface
+ interface A9 extends StepDefinitionBody {
+ void accept(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9) throws Throwable;
+
+ }
+
+ }
+
+}
diff --git a/java-lambda/src/main/java/io/cucumber/lambda/StepDefinitions.java b/java-lambda/src/main/java/io/cucumber/lambda/StepDefinitions.java
new file mode 100644
index 0000000000..550240c00a
--- /dev/null
+++ b/java-lambda/src/main/java/io/cucumber/lambda/StepDefinitions.java
@@ -0,0 +1,132 @@
+package io.cucumber.lambda;
+
+import io.cucumber.lambda.StepDefinitionFunction.C1A0;
+import io.cucumber.lambda.StepDefinitionFunction.C1A1;
+import io.cucumber.lambda.StepDefinitionFunction.C1A2;
+import io.cucumber.lambda.StepDefinitionFunction.C1A3;
+import io.cucumber.lambda.StepDefinitionFunction.C1A4;
+import io.cucumber.lambda.StepDefinitionFunction.C1A5;
+import io.cucumber.lambda.StepDefinitionFunction.C1A6;
+import io.cucumber.lambda.StepDefinitionFunction.C1A7;
+import io.cucumber.lambda.StepDefinitionFunction.C1A8;
+import io.cucumber.lambda.StepDefinitionFunction.C1A9;
+import org.apiguardian.api.API;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.apiguardian.api.API.Status.EXPERIMENTAL;
+
+@API(status = EXPERIMENTAL, since = "7.4.0")
+public final class StepDefinitions {
+
+ private final List stepDeclarations;
+ StepDefinitions(List stepDefinitions) {
+ this.stepDeclarations = stepDefinitions;
+ }
+
+ List getStepDeclarations() {
+ return stepDeclarations;
+ }
+
+ public static Builder using(Class context) {
+ return new Builder<>(context);
+ }
+
+ public static final class Builder {
+
+ private final List stepDefinitions = new ArrayList<>();
+ private final Class context;
+
+ Builder(Class context) {
+ this.context = context;
+ }
+
+ public StepDefinitions build() {
+ return new StepDefinitions(stepDefinitions);
+ }
+
+ public Builder step(String expression, C1A0 stepDefinitionFunction) {
+ StackTraceElement location = new Exception().getStackTrace()[1];
+ stepDefinitions.add(new StepDeclaration(expression, context, stepDefinitionFunction, location));
+ return this;
+ }
+
+ public Builder step(String expression, C1A1 stepDefinitionFunction) {
+ StackTraceElement location = new Exception().getStackTrace()[1];
+ stepDefinitions.add(new StepDeclaration(expression, context, stepDefinitionFunction, location));
+ return this;
+ }
+
+ public Builder step(String expression, C1A2 stepDefinitionFunction) {
+ StackTraceElement location = new Exception().getStackTrace()[1];
+ stepDefinitions.add(new StepDeclaration(expression, context, stepDefinitionFunction, location));
+ return this;
+ }
+
+ public Builder step(
+ String expression,
+ C1A3 stepDefinitionFunction
+ ) {
+ StackTraceElement location = new Exception().getStackTrace()[1];
+ stepDefinitions.add(new StepDeclaration(expression, context, stepDefinitionFunction, location));
+ return this;
+ }
+
+ public Builder step(
+ String expression,
+ C1A4 stepDefinitionFunction
+ ) {
+ StackTraceElement location = new Exception().getStackTrace()[1];
+ stepDefinitions.add(new StepDeclaration(expression, context, stepDefinitionFunction, location));
+ return this;
+ }
+
+ public Builder step(
+ String expression,
+ C1A5 stepDefinitionFunction
+ ) {
+ StackTraceElement location = new Exception().getStackTrace()[1];
+ stepDefinitions.add(new StepDeclaration(expression, context, stepDefinitionFunction, location));
+ return this;
+ }
+
+ public Builder step(
+ String expression,
+ C1A6 stepDefinitionFunction
+ ) {
+ StackTraceElement location = new Exception().getStackTrace()[1];
+ stepDefinitions.add(new StepDeclaration(expression, context, stepDefinitionFunction, location));
+ return this;
+ }
+
+ public Builder step(
+ String expression,
+ C1A7 stepDefinitionFunction
+ ) {
+ StackTraceElement location = new Exception().getStackTrace()[1];
+ stepDefinitions.add(new StepDeclaration(expression, context, stepDefinitionFunction, location));
+ return this;
+ }
+
+ public Builder step(
+ String expression,
+ C1A8 stepDefinitionFunction
+ ) {
+ StackTraceElement location = new Exception().getStackTrace()[1];
+ stepDefinitions.add(new StepDeclaration(expression, context, stepDefinitionFunction, location));
+ return this;
+ }
+
+ public Builder step(
+ String expression,
+ C1A9 stepDefinitionFunction
+ ) {
+ StackTraceElement location = new Exception().getStackTrace()[1];
+ stepDefinitions.add(new StepDeclaration(expression, context, stepDefinitionFunction, location));
+ return this;
+ }
+
+ }
+
+}
diff --git a/java-lambda/src/test/java/io/cucumber/lambda/InvokerTest.java b/java-lambda/src/test/java/io/cucumber/lambda/InvokerTest.java
new file mode 100644
index 0000000000..4c5b519766
--- /dev/null
+++ b/java-lambda/src/test/java/io/cucumber/lambda/InvokerTest.java
@@ -0,0 +1,65 @@
+package io.cucumber.lambda;
+
+import io.cucumber.core.backend.CucumberBackendException;
+import io.cucumber.core.backend.CucumberInvocationTargetException;
+import io.cucumber.core.backend.Located;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.function.Executable;
+
+import java.lang.reflect.Method;
+import java.util.function.Function;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.*;
+
+class InvokerTest {
+
+ final Located located = new Located() {
+ @Override
+ public boolean isDefinedAt(StackTraceElement stackTraceElement) {
+ return false;
+ }
+
+ @Override
+ public String getLocation() {
+ return "io.cucumber.lambda.InvokerTest.example(InvokerTest.java:1)";
+ }
+ };
+
+ final Function