-1

I'm trying to create an implementation that allows the use of IOException through the method, but something weird occurs

/**
 * Represents a predicate (boolean-valued function) of two arguments. This is the two-arity specialization of {@link Predicate}.
 *
 * <p>
 * This interface is similar to {@link BiPredicate} except that it is allowed to throw an {@link IOException}.
 * </p>
 *
 * @param <T> the type of the first argument to the predicate
 * @param <U> the type of the second argument the predicate
 *
 * @author Magno N A Cruz
 * @see Predicate
 */
@FunctionalInterface
public interface IOBiPredicate<T, U> {

/**
 * Evaluates this predicate on the given arguments.
 *
 * @param t the first input argument
 * @param u the second input argument
 * @return {@code true} if the input arguments match the predicate, otherwise {@code false}
 * @throws IOException if there is an I/O error performing the operation.
 */
boolean test(T t, U u) throws IOException;

/**
 * Returns a composed predicate that represents a short-circuiting logical AND of this predicate and another. When evaluating the composed predicate, if this
 * predicate is {@code false}, then the {@code other} predicate is not evaluated.
 *
 * <p>
 * Any exceptions thrown during evaluation of either predicate are relayed to the caller; if evaluation of this predicate throws an exception, the
 * {@code other} predicate will not be evaluated.
 * </p>
 *
 * @param other a predicate that will be logically-ANDed with this predicate
 * @return a composed predicate that represents the short-circuiting logical AND of this predicate and the {@code other} predicate
 * @throws NullPointerException if other is null
 */
default IOBiPredicate<T, U> and(BiPredicate<? super T, ? super U> other) {
    Objects.requireNonNull(other);
    return (T t, U u) -> test(t, u) && other.test(t, u);
}

/**
 * Returns a predicate that represents the logical negation of this predicate.
 *
 * @return a predicate that represents the logical negation of this predicate
 */
default IOBiPredicate<T, U> negate() {
    return (T t, U u) -> !test(t, u);
}

/**
 * Returns a composed predicate that represents a short-circuiting logical OR of this predicate and another. When evaluating the composed predicate, if this
 * predicate is {@code true}, then the {@code other} predicate is not evaluated.
 *
 * <p>
 * Any exceptions thrown during evaluation of either predicate are relayed to the caller; if evaluation of this predicate throws an exception, the
 * {@code other} predicate will not be evaluated.
 * </p>
 *
 * @param other a predicate that will be logically-ORed with this predicate
 * @return a composed predicate that represents the short-circuiting logical OR of this predicate and the {@code other} predicate
 * @throws NullPointerException if other is null
 */
default BiPredicate<T, U> or(BiPredicate<? super T, ? super U> other) {
    Objects.requireNonNull(other);
    return (T t, U u) -> test(t, u) || other.test(t, u);
}

}

on the lambda expression (T t, U u) -> test(t, u) || other.test(t, u), the first part of the condition i.e., test(t, u) gives me a compiler error saying Unhandled exception type IOException but the second one i.e., other.test(t, u) seems to work fine, so it's a puzzling situation for me, why does it occur to one but not to the other?


Is there a clean solution for that? If not, how could I fix it without having kludges on the program?

This question is similar to Java 8 Lambda function that throws exception?, but for simple functions shown in that question it works and for others that have a conditional like this BiPredicate don't.

Community
  • 1
  • 1
Magno Nascimento
  • 615
  • 4
  • 19
  • Will your problem be solved if you add `throws IOException` to the `or` method? or catch the exception in the lambda function? – jrook Apr 30 '17 at 06:49
  • "Notice that I've just added a throws declaration from the original one." And this is the problem. `BiPredicate.test(...)` does not throw an `Exception` as it is defined. Therefore, your implementation cannot throw an `Exception` as well (unless it `extends RuntimeException`). Otherwise, the catch-or-throws rule cannot be ensured. – Turing85 Apr 30 '17 at 06:51
  • no, the problem isn't on the `or` method, and if I catch the exception in the lambda I'd have to rethrow it as an `UncheckedException`, and I just want that if it's the last possible option as I'm trying to keep the `IOException` being thrown. – Magno Nascimento Apr 30 '17 at 06:53
  • But @Turing85, that doesn't explain why just one half of the condition works with `IOException`.. if I delete `test(t, u)` and keep `(T t, U u) -> other.test(t, u)` it will work without any problem :/ – Magno Nascimento Apr 30 '17 at 06:57
  • The question is answered here http://stackoverflow.com/questions/18198176/java-8-lambda-function-that-throws-exception – frhack Apr 30 '17 at 06:58
  • `other` is a BiPredicte, not an IOBiPredicate. A BiPredicate doesn't throw any IOException. – JB Nizet Apr 30 '17 at 06:58
  • 1
    Please create a [MCVE](https://stackoverflow.com/help/mcve]). As I see it, `other` is a `BiPredicate` and thus `other.test(...)` does not throw an `Exception`. I would further conclude from your description that `IOBiPredicate extends BiPredicate`? – Turing85 Apr 30 '17 at 06:59
  • 1
    @frhack it's not, the type in question (`IOBiPredicate`) *does* permit throwing a checked `IOException`. – dimo414 Apr 30 '17 at 07:07

1 Answers1

2

Notice that in (T t, U u) -> test(t, u) || other.test(t, u); the test() method is on your IOBiPredicate (and therefore is specified as throws IOException), while other.test() is calling BiPredicate.test(), which does not. That's why only the first half of the lambda is causing a compiler error.

Since your IOBiPredicate.or() method doesn't specify that it throws IOException you get an error when you call test(), since it can throw IOException.

You should add throws IOException to or(), and possibly make other a IOBiPredicate, not a BiPredicate. A caller with a BiPredicate can always convert it to an IOBiPredicate by passing in the method reference bipred::test to the method that expects an IOBiPredicate.

The underlying issue is you appear to be mixing BiPredicate and IOBiPredicate (perhaps you made IOBiPredicate extend BiPredicate?), however they are not safely interchangeable. You cannot pass a IOBiPredicate instance into a method that takes a BiPredicate, since IOBiPredicate will throw IOException, which code that takes a BiPredicate will not handle. This is an example of a violation of the Liskov Substitution Principle.

dimo414
  • 47,227
  • 18
  • 148
  • 244
  • The `test` method with the checked exception is called within the body of a lamda, so I am pretty sure you have to catch it, making `or` throw an `IOException` won't help – Trash Can Apr 30 '17 at 07:01
  • The lambda is of type `IOBiPredicate` (it's the return type of the `IOBiPredicate.or()` method), which can throw `IOException`. If `IOBiPredicate.or()` simply returned a `BiPredicate` that would be a problem. There's nothing inherently wrong with a lamdba expression that throws checked exceptions - but lambdas that implement the common interfaces found in `java.util.function` cannot, since *their* signatures don't permit checked exceptions. – dimo414 Apr 30 '17 at 07:03
  • Your answer seems to be half correct, I saw that I made the silly mistake of mixing things there.. But you're wrong about the polymorphism, I'm not extending anything, I'm creating my own implementation of that. I'll check for the other conditionals and see if I can solve this problem. – Magno Nascimento Apr 30 '17 at 07:09
  • 2
    @MagnoNascimento it would be so eacy to understand and answer if you posted a minimal, complete example, instead of some partial snippets where we need to guess the remaining parts. – JB Nizet Apr 30 '17 at 07:12
  • @JBNizet, I added it, thank you for the tip.. I'm used to proprietary code, so sometimes I just forget that i'm messing with an open-source one. – Magno Nascimento Apr 30 '17 at 07:17
  • 1
    @MagnoNascimento Ah. So you changed the code. Your or() method returns a BiPredicate, not an IOBiPredicate. A BiPredicate's test() method may not throw any checked exception. But test(t, u) does throw an IOException. or() must return an IOBiPredicate, of it must catch the IOException. – JB Nizet Apr 30 '17 at 07:20
  • @MagnoNascimento without the full class I had to make an educated guess. – dimo414 Apr 30 '17 at 17:54
  • @dimo414, I know.. sorry if I was rude, your answer really helped me with that silly mistake. thank you. – Magno Nascimento Apr 30 '17 at 17:57