diff --git a/functional-programming-java/src/main/java/org/gym/fp/fpjava/collection/List.java b/functional-programming-java/src/main/java/org/gym/fp/fpjava/collection/List.java
new file mode 100644
index 0000000..464d9e3
--- /dev/null
+++ b/functional-programming-java/src/main/java/org/gym/fp/fpjava/collection/List.java
@@ -0,0 +1,179 @@
+package org.gym.fp.fpjava.collection;
+
+import org.gym.fp.fpjava.type.Function;
+import org.gym.fp.fpjava.type.TailCall;
+
+import static org.gym.fp.fpjava.type.TailCall.ret;
+import static org.gym.fp.fpjava.type.TailCall.suspend;
+
+public abstract class List {
+
+ public abstract A head();
+
+ public abstract List tail();
+
+ public abstract boolean isEmpty();
+
+ public abstract List setHead(A a);
+
+ public abstract List drop(int n);
+
+ public abstract List dropWhile(Function predicate);
+
+ public abstract List reverse();
+
+ public abstract List init();
+
+ public List cons(A a) {
+ return new Cons<>(a, this);
+ }
+
+ @SuppressWarnings("rawtypes")
+ public static final List NIL = new Nil();
+
+ private List() {
+ }
+
+ private static class Nil extends List {
+
+ private Nil() {
+ }
+
+ @Override
+ public A head() {
+ throw new IllegalStateException("head called en empty list");
+ }
+
+ @Override
+ public List tail() {
+ throw new IllegalStateException("tail called en empty list");
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return true;
+ }
+
+ @Override
+ public List setHead(A a) {
+ throw new IllegalStateException("setHead called on empty list");
+ }
+
+ @Override
+ public List drop(int n) {
+ return this;
+ }
+
+ @Override
+ public List dropWhile(Function predicate) {
+ return this;
+ }
+
+ @Override
+ public List reverse() {
+ return this;
+ }
+
+ @Override
+ public List init() {
+ throw new IllegalStateException("init called on empty list");
+ }
+
+ @Override
+ public String toString() {
+ return "[NIL]";
+ }
+ }
+
+ private static class Cons extends List {
+ private final A head;
+ private final List tail;
+
+ private Cons(A head, List tail) {
+ this.head = head;
+ this.tail = tail;
+ }
+
+ @Override
+ public A head() {
+ return head;
+ }
+
+ @Override
+ public List tail() {
+ return tail;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return false;
+ }
+
+ @Override
+ public List setHead(A h) {
+ return new Cons<>(h, tail());
+ }
+
+ @Override
+ public List drop(int n) {
+ return n <= 0 ? this : drop(this, n).eval();
+ }
+
+ @Override
+ public List dropWhile(Function predicate) {
+ return dropWhile(this, predicate).eval();
+ }
+
+ @Override
+ public List reverse() {
+ return reverse(list(), this).eval();
+ }
+
+ @Override
+ public List init() {
+ return reverse().tail().reverse();
+ }
+
+ public TailCall> reverse(List acc, List list) {
+ return list.isEmpty()
+ ? ret(acc)
+ : suspend(() -> reverse(new Cons<>(list.head(), acc), list.tail()));
+ }
+
+ private TailCall> dropWhile(List list, Function predicate) {
+ return !list.isEmpty() && predicate.apply(list.head())
+ ? suspend(() -> dropWhile(list.tail(), predicate))
+ : ret(list);
+ }
+
+ private TailCall> drop(List list, int n) {
+ return n <= 0 || list.isEmpty()
+ ? ret(list)
+ : suspend(() -> drop(list.tail(), n - 1));
+ }
+
+ @Override
+ public String toString() {
+ return String.format("[%sNIL]", toString(new StringBuilder(), this).eval());
+ }
+
+ private TailCall toString(StringBuilder acc, List list) {
+ return list.isEmpty()
+ ? ret(acc)
+ : suspend(() -> toString(acc.append(list.head()).append(", "), list.tail()));
+ }
+ }
+
+ public static List list() {
+ return NIL;
+ }
+
+ @SafeVarargs
+ public static List list(A... elements) {
+ List n = list();
+ for (int i = elements.length - 1; i >= 0; i--) {
+ n = new Cons<>(elements[i], n);
+ }
+ return n;
+ }
+}