Interface RetVal<T>

  • Type Parameters:
    T - type of the contained value.
    All Superinterfaces:
    ProblemContainer, ValuedProblemContainer<T>
    All Known Implementing Classes:
    MonitoredReturnProblem, MonitoredReturnValue, SimpleReturnProblem, SimpleReturnValue


    public interface RetVal<T>
    extends ValuedProblemContainer<T>, ProblemContainer
    A non-null value holder version of a problem container.

    Instances are intended to provide to other Ret* classes a value or problem, or to provide to the using class a value or problems. These instances represent a correct or wrong environment that went into an expected value. To that end, all methods that chain to this one will not throw away the value; it will either be returned or offered to a parameter function.

    For simple cases, this is useful for return a validated value:

         RetVal<String> loadEmail() {
             String email = readEmail();
             if (! isValidEmailFormat(email)) {
                 return RetVal.fromProblem(LocalProblem.from("not valid email format: " + email));
             }
             return RetVal.ok(email);
         }
     

    In many cases, several validation checks may be necessary to build up a complex value. In this case, the values can be chained together:

         RetVal<String> loadEmailForUser(String userName) {
             return
                     findEmailForUser(userName)
                 .then((email) ->
                     isValidEmailFormat(email)
                         ? RetVal.ok(email)
                         : RetVal.fromProblem(LocalProblem.from("not valid email format: " + email))
                  );
         }
     

    If the value is an intermediary value, used for constructing other values, then you may need to return the value early, but that may require casting it to another type:

         RetVal<User> loadUser(String userName) {
             RetVal<String> emailRes = loadEmail(userName);
             if (emailRes.hasProblems()) {
                 return emailRes.forwardProblems();
             }
             // ... more data loading and validation
         }
     

    For use cases where the value may be partially constructed while including problems, see the WarningVal class.

    • Method Detail

      • ok

        @Nonnull
        static <T> RetVal<T> ok​(@Nonnull
                                T value)
        Create a new RetVal instance that has a value and no problems.
        Type Parameters:
        T - type of the value.
        Parameters:
        value - non-null value.
        Returns:
        a RetVal containing the value.
        Throws:
        java.lang.NullPointerException - if the value is null.
      • fromProblem

        @Nonnull
        static <T> RetVal<T> fromProblem​(@Nonnull
                                         Problem problem,
                                         @Nonnull
                                         Problem... problems)
        Create a new RetVal instance that has problems.
        Type Parameters:
        T - type of the value
        Parameters:
        problem - the first problem.
        problems - optional list of other problems to include in this value.
        Returns:
        a problem RetVal.
      • fromProblem

        @SafeVarargs
        @Nonnull
        static <T> RetVal<T> fromProblem​(@Nonnull
                                         java.util.Collection<Problem> problem,
                                         @Nonnull
                                         java.util.Collection<Problem>... problems)
        Create a new RetVal instance that has problems stored in collections of problems. The arguments must contain at least one problem.
        Type Parameters:
        T - type of the value
        Parameters:
        problem - the first problem.
        problems - optional list of other problems to include in this value.
        Returns:
        a problem RetVal.
        Throws:
        java.lang.IllegalArgumentException - if no problems exist within the arguments.
      • fromProblems

        @Nonnull
        static <T> RetVal<T> fromProblems​(@Nonnull
                                          ProblemContainer problem,
                                          @Nonnull
                                          ProblemContainer... problems)
        Create a new RetVal instance that has problems. This is only valid if at least one problem exists within all the arguments.

        Normally, you would use this in situations where you collect several validations together, when you know at least one of them has a problem, if not more.

        Type Parameters:
        T - type of the value
        Parameters:
        problem - the first problem container.
        problems - optional list of other problem containers to include in this value.
        Returns:
        a problem RetVal.
        Throws:
        java.lang.IllegalArgumentException - if no problems exist within the arguments.
      • fromProblems

        @SafeVarargs
        @Nonnull
        static <T> RetVal<T> fromProblems​(@Nonnull
                                          java.util.Collection<ProblemContainer> problem,
                                          @Nonnull
                                          java.util.Collection<ProblemContainer>... problems)
        Create a new RetVal instance that has problems. This is only valid if at least one problem exists within all the arguments.

        Normally, you would use this in situations where you collect several validations together, when you know at least one of them has a problem, if not more.

        Type Parameters:
        T - type of the value
        Parameters:
        problem - the first problem container.
        problems - optional list of other problem containers to include in this value.
        Returns:
        a problem RetVal.
        Throws:
        java.lang.IllegalArgumentException - if no problems exist within the arguments.
      • getValue

        @Nullable
        T getValue​()
        Get the value contained in this instance. If this is an problem state, then the value will be null.

        This is usually helpful for log messages where a correctness check would impose extra runtime overhead that logging doesn't care about. This can also be used to return a simple value to other parts of a program that use null to indicate an invalid setup but that are separate from user notifications.

        Specified by:
        getValue in interface ValuedProblemContainer<T>
        Returns:
        the value, which will be null if there are problems.
      • asOptional

        @Nonnull
        java.util.Optional<T> asOptional​()
        Convert the value into an optional typed value. Note that doing this will lose any problem state, so checking for problems should be done before calling this.

        This function has limited use. It's provided here to allow support for systems that use Optional values.

        Returns:
        the value as an optional type. No checks are made against the problem state.
      • requireOptional

        @Nonnull
        java.util.Optional<T> requireOptional​()
        Convert the value into an optional typed value only if there are no problems.
        Returns:
        the value as an optional type. No checks are made against the problem state.
      • result

        @Nonnull
        T result​()
        Get the result, which is always non-null. If this instance has problems, then a runtime exception is thrown. Therefore, it's necessary to perform a validity check before calling.
        Returns:
        the non-null value, only if this instance is ok.
        Throws:
        java.lang.IllegalStateException - if this instance has problems.
      • forwardProblems

        @Nonnull
        <V> RetVal<V> forwardProblems​()
        Forward this object to a different typed RetVal instance. This will only work when the instance has problems.

        The most common use case is when a value construction requires multiple steps, and an early step requires early exit from the function. This allows a memory efficient type casting of the problems to the construction function's type.

        Type Parameters:
        V - altered type.
        Returns:
        the type-altered version
        Throws:
        java.lang.IllegalStateException - if this instance does not have problems.
      • forwardNullableProblems

        @Nonnull
        <V> RetNullable<V> forwardNullableProblems​()
        Forward this instance as a nullable with a different value type, but only if it has problems. If it does not have problems, then a runtime exception is thrown.

        The most common use case is when a value construction requires multiple steps, and an early step requires early exit from the function. This allows a memory efficient type casting of the problems to the construction function's type.

        Type Parameters:
        V - destination type
        Returns:
        the value, only if this instance has problems.
      • forwardVoidProblems

        @Nonnull
        RetVoid forwardVoidProblems​()
        Forward this instance as a value-less object, but only if it has problems. If it does not have problems, then a runtime exception is thrown.

        The most common use case is when a value construction requires multiple steps, and an early step requires early exit from the function. This allows a memory efficient type casting of the problems to the construction function's type.

        Returns:
        the value, only if this instance has problems.
      • asNullable

        @Nonnull
        RetNullable<T> asNullable​()
        Convert this instance into a nullable instance with the same value type. If this instance has problems, they are returned. This differs from forwardNullableProblems() by 1. keeping the same type, and 2. allowing for a non-problem state in the returned value.

        A convenience function for situations where a receiver can expect valid uses of a null value when the source is known to never return a null value.

        Returns:
        a nullable version of the same instance.
      • thenValidate

        @Nonnull
        RetVal<T> thenValidate​(@Nonnull
                               NonnullParamFunction<T,ProblemContainer> checker)
        Validate the value in the checker. The checker can perform any amount of validation against the value as necessary, and returns an optional container of problems. If no problem is found, the checker can return null. The checker is only invoked if the value has no current problems.

        Be careful with over-using this method. ValueBuilder and ProblemCollector provide better solutions for constructing validation problems within a method. This particular method finds practical use when a builder method returns an initially valid value, then another method performs cross-value validation.

        Parameters:
        checker - function that checks the validity of the value.
        Returns:
        a retval with additional problems, or this value if no problem is found.
      • then

        @Nonnull
        <R> RetVal<R> then​(@Nonnull
                           NonnullFunction<T,RetVal<R>> func)
        Return a non-null RetVal value using a function that itself returns a RetVal, taking the current non-nullable value as an argument. The function is called only if this object has no problem. If it has a problem, then the current list of problems is returned as the new type.

        This is similar to map(NonnullFunction), but logically different. This method implies the functional argument will use the value and perform new processing to create a different value.

        Type Parameters:
        R - type of the returned value.
        Parameters:
        func - functional object that returns a RetVal and takes the current value as argument.
        Returns:
        the problems of the current value, or the object returned by the function if there are no problems (but the returned value may have its own set of problems).
      • map

        @Nonnull
        <R> RetVal<R> map​(@Nonnull
                          NonnullFunction<T,R> func)
        Return a non-null RetVal value, using a function that returns a non-null value, taking the current non-null value as an argument. The function is called only if this object has no problem. If it has a problem, then the current list of problems is returned as the new type.

        This is similar to then(NonnullFunction), but logically different. This method implies the functional argument performs a transformation of the data type into another one without validation checks.

        Type Parameters:
        R - type of the returned value.
        Parameters:
        func - functional object that takes the current value as argument, and returns a transformed value.
        Returns:
        the problem of the current value, if it is an problem, or the object returned by the function.
      • thenNullable

        @Nonnull
        <R> RetNullable<R> thenNullable​(@Nonnull
                                        NonnullFunction<T,RetNullable<R>> func)
        Return a non-null RetNullable value using a function that itself returns a RetNullable, taking the current non-null value as an argument. The function is called only if this object has no problem.

        This is similar to mapNullable(NonnullParamFunction), but logically different. This method implies the functional argument will use the value and perform new processing to create a different value.

        Type Parameters:
        R - type of the returned value.
        Parameters:
        func - functional object that returns a RetNullable and takes the current value as argument.
        Returns:
        the problem of the current value, if it is a problem, or the object returned by the supplier.
      • mapNullable

        @Nonnull
        <R> RetNullable<R> mapNullable​(@Nonnull
                                       NonnullParamFunction<T,R> func)
        If this instance has no problems, then it runs the parameter with the current value. The returned value, which may be null, is wrapped in a nullable value. If this instance has problems, then the problem list is returned and the parameter is not run.

        This is similar to thenNullable(NonnullFunction), but logically different. This method implies the functional argument performs a transformation of the data type into another one without validation checks.

        Type Parameters:
        R - return value type
        Parameters:
        func - the function to run.
        Returns:
        a transformed version of this object.
      • thenRun

        @Nonnull
        RetVal<T> thenRun​(@Nonnull
                          java.lang.Runnable runner)
        Run the parameter, only if this instance has no problems.
        Parameters:
        runner - the runnable to execute if no problems exist
        Returns:
        this instance
      • thenRun

        @Nonnull
        RetVal<T> thenRun​(@Nonnull
                          NonnullConsumer<T> consumer)
        Run the consumer with the current value, only if this instance has no problems.
        Parameters:
        consumer - the consumer of this value to run if no problems exist
        Returns:
        this instance.
      • thenVoid

        @Nonnull
        RetVoid thenVoid​(@Nonnull
                         NonnullConsumer<T> consumer)
        Pass the value of this instance to the consumer, only if there are no problems. Return a void version of this instance.

        A note about usage: if the argument is a lambda that ignores the argument, then the compiler will fail due to an ambiguous call. There exist some use cases where the argument value is no longer needed and can be safely ignored; for those scenarios, use consume(NonnullConsumer).

        This call will lose the contained value on return, so it's used to pass on the value to another object.

        Parameters:
        consumer - consumer of this value.
        Returns:
        a response that contains the problem state of the current value.
      • thenVoid

        @Nonnull
        RetVoid thenVoid​(@Nonnull
                         NonnullFunction<T,RetVoid> func)
        Pass the value of this instance to the function, only if there are no problems. Return a void version of this instance if there are problems with it, or the return value from the function.

        A note about usage: if the argument is a lambda that ignores the argument, then the compiler will fail due to an ambiguous call. There exist some use cases where the argument value is no longer needed and can be safely ignored; for those scenarios, use produceVoid(NonnullFunction).

        This call will lose the contained value on return, so it's used to pass on the value to another object.

        Parameters:
        func - consumer of this value.
        Returns:
        the result of the function, if there are no problems, otherwise a RetVoid with this instance's problems.
      • consume

        @Nonnull
        RetVoid consume​(@Nonnull
                        NonnullConsumer<T> consumer)
        Pass the value of this instance to the consumer, only if there are no problems. Return a void version of this instance.

        This call will lose the contained value on return, so it's used to pass on the value to another object.

        Parameters:
        consumer - consumer of this value.
        Returns:
        a response that contains the problem state of the current value.
        Since:
        2.1
      • produceVoid

        @Nonnull
        RetVoid produceVoid​(@Nonnull
                            NonnullFunction<T,RetVoid> func)
        Pass the value of this instance to the consumer, only if there are no problems. Return the function's value.

        This call will lose the contained value on return, so it's used to pass on the value to another object.

        Parameters:
        func - consumer of this value.
        Returns:
        a response that contains the problem state of the current value.
        Since:
        2.1