uk.kludje.Nullifier Maven / Gradle / Ivy
/*
* Copyright 2015 McDowell
*
* 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 uk.kludje;
import java.util.Objects;
/**
* A functional interface with null-safe checks.
* For use with getter chains where one or more elements in the chain
* can be null.
*
* Example usage:
* D d = Nullifier.eval(a, A::getB, B::getC, C::getD);
*
* The above code is equivalent to:
*
* D d = null;
* if (a != null) {
* B b = a.getB();
* if (b != null) {
* C c = b.getC();
* if (c != null) {
* d = c.getD();
* }
* }
* }
*
*
* To test if d
is null, use:
* boolean dIsNull = Nullifier.isNull(a, A::getB, B::getC, C::getD);
*
* Implement {@link #$apply(Object)}; invoke {@link #apply(Object)}.
*
* @param the input
* @param the result
*/
@FunctionalInterface
public interface Nullifier {
/**
* Creates a null-safe chain of calls spanning possibly null call sites.
*
* The functions may not be null, but the inputs and outputs may be.
*
* A number of overloaded methods are provided with varying argument counts.
*
* @param f0 the initial function; MUST NOT be null
* @param f1 a subsequent function; MUST NOT be null
* @param the initial type
* @param an intermediary type
* @param the resultant type
* @return a function that, given A, returns Z, or null if any element in the chain is null
*/
public static Nullifier span(Nullifier super A, ? extends B> f0,
Nullifier super B, ? extends Z> f1) {
Objects.requireNonNull(f0, "0");
Objects.requireNonNull(f1, "1");
return a -> eval(a, f0, f1);
}
public static Nullifier span(Nullifier super A, ? extends B> f0,
Nullifier super B, ? extends C> f1,
Nullifier super C, ? extends Z> f2) {
Objects.requireNonNull(f0, "0");
Objects.requireNonNull(f1, "1");
Objects.requireNonNull(f2, "2");
return a -> eval(a, f0, f1, f2);
}
public static Nullifier span(Nullifier super A, ? extends B> f0,
Nullifier super B, ? extends C> f1,
Nullifier super C, ? extends D> f2,
Nullifier super D, ? extends Z> f3) {
Objects.requireNonNull(f0, "0");
Objects.requireNonNull(f1, "1");
Objects.requireNonNull(f2, "2");
Objects.requireNonNull(f3, "3");
return a -> eval(a, f0, f1, f2, f3);
}
public static Nullifier span(Nullifier super A, ? extends B> f0,
Nullifier super B, ? extends C> f1,
Nullifier super C, ? extends D> f2,
Nullifier super D, ? extends E> f3,
Nullifier super E, ? extends Z> f4) {
Objects.requireNonNull(f0, "0");
Objects.requireNonNull(f1, "1");
Objects.requireNonNull(f2, "2");
Objects.requireNonNull(f3, "3");
Objects.requireNonNull(f4, "4");
return a -> eval(a, f0, f1, f2, f3, f4);
}
public static Z eval(A a,
Nullifier super A, ? extends Z> f0) {
return f0.apply(a);
}
/**
* Convenience method for evaluating a chain of {@link Nullifier} calls.
*
* A number of overloaded methods are provided with varying argument counts.
*
* @param a the root object in the object graph (may be null)
* @param f0 is passed "a"; MUST NOT be null
* @param f1 is passed the result of "f0"; MUST NOT be null
* @param the root type
* @param an intermediary type
* @param the result type
* @return the result of the function chain or null
* @see #eval(Object, Nullifier)
* @see #eval(Object, Nullifier, Nullifier)
* @see #eval(Object, Nullifier, Nullifier, Nullifier)
* @see #eval(Object, Nullifier, Nullifier, Nullifier, Nullifier, Nullifier)
*/
public static Z eval(A a,
Nullifier super A, ? extends B> f0,
Nullifier super B, ? extends Z> f1) {
B b = f0.apply(a);
return f1.apply(b);
}
public static Z eval(A a,
Nullifier super A, ? extends B> f0,
Nullifier super B, ? extends C> f1,
Nullifier super C, ? extends Z> f2) {
B b = f0.apply(a);
C c = f1.apply(b);
return f2.apply(c);
}
public static Z eval(A a,
Nullifier super A, ? extends B> f0,
Nullifier super B, ? extends C> f1,
Nullifier super C, ? extends D> f2,
Nullifier super D, ? extends Z> f3) {
B b = f0.apply(a);
C c = f1.apply(b);
D d = f2.apply(c);
return f3.apply(d);
}
public static Z eval(A a,
Nullifier super A, ? extends B> f0,
Nullifier super B, ? extends C> f1,
Nullifier super C, ? extends D> f2,
Nullifier super D, ? extends E> f3,
Nullifier super E, ? extends Z> f4) {
B b = f0.apply(a);
C c = f1.apply(b);
D d = f2.apply(c);
E e = f3.apply(d);
return f4.apply(e);
}
public static boolean isNull(A a,
Nullifier super A, ? extends Z> f0) {
return f0.apply(a) == null;
}
/**
* Convenience method for evaluating a chain of {@link Nullifier} calls to see if any link is null.
* A number of overloaded methods are provided with varying argument counts.
* Equivalent to:
* boolean isNull = (Nullifier.eval(a, f0, f1) == null);
*
* @param a the root object in the object graph (may be null)
* @param f0 is passed "a"; MUST NOT be null
* @param f1 is passed the result of "f0"; MUST NOT be null
* @param the root type
* @param an intermediary type
* @param the result type
* @return true if the result is null; false otherwise
* @see #isNull(Object, Nullifier)
* @see #isNull(Object, Nullifier, Nullifier)
* @see #isNull(Object, Nullifier, Nullifier, Nullifier)
* @see #isNull(Object, Nullifier, Nullifier, Nullifier, Nullifier, Nullifier)
*/
public static boolean isNull(A a,
Nullifier super A, ? extends B> f0,
Nullifier super B, ? extends Z> f1) {
return eval(a, f0, f1) == null;
}
public static boolean isNull(A a,
Nullifier super A, ? extends B> f0,
Nullifier super B, ? extends C> f1,
Nullifier super C, ? extends Z> f2) {
return eval(a, f0, f1, f2) == null;
}
public static boolean isNull(A a,
Nullifier super A, ? extends B> f0,
Nullifier super B, ? extends C> f1,
Nullifier super C, ? extends D> f2,
Nullifier super D, ? extends Z> f3) {
return eval(a, f0, f1, f2, f3) == null;
}
public static boolean isNull(A a,
Nullifier super A, ? extends B> f0,
Nullifier super B, ? extends C> f1,
Nullifier super C, ? extends D> f2,
Nullifier super D, ? extends E> f3,
Nullifier super E, ? extends Z> f4) {
return eval(a, f0, f1, f2, f3, f4) == null;
}
/**
* If the argument is null, returns null; else invokes {@link #$apply(Object)}.
*
* This method rethrows any exception thrown by {@link #$apply(Object)} as an unchecked exception.
*
* @param t the input which may be null
* @return the result which may be null
* @see Exceptions#throwChecked(Throwable)
*/
default R apply(T t) {
try {
return (t == null) ? null : $apply(t);
} catch (Exception e) {
throw Exceptions.throwChecked(e);
}
}
/**
* Implement this method with a lambda expression/method reference.
*
* Consumers should invoke {@link #apply(Object)} and NOT call this method directly.
*
* @param t the argument; not null if invoked by default {@link #apply(Object)}
* @return the result
* @throws Exception on error
*/
R $apply(T t) throws Exception;
/**
* Chains two instances together.
*
* @param after the nullifier to invoke after this; MUST NOT be null
* @param the new result type
* @return a new nullifier
*/
default Nullifier andThenSpan(Nullifier super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
}