2

This code seems logical to me, why the compiler is complaining about the predicate isBondAsset having the wrong type ?

I mean a "Predicate of any type that extends Asset" is a "Predicate of any type super of any type that extends Asset" or i'm wrong ?

private static class Asset
{
    public enum AssetType
    {
        BOND,
        STOCK;
    }

    private final AssetType type;
    private final int value;

    public Asset (AssetType type, int value)
    {
        this.type = type;
        this.value = value;
    }

    public AssetType getType ()
    {
        return type;
    }

    public int getValue ()
    {
        return value;
    }
}

private static class AssetUtils
{
    public static int totalBondAssetValues (Collection <? extends Asset> assets)
    {
        Predicate<? extends Asset> isBondAsset = asset -> Asset.AssetType.BOND.equals(asset.getType());

        return assets.stream().filter(isBondAsset).mapToInt(Asset::getValue).sum();
    }
}
Misha
  • 25,007
  • 4
  • 52
  • 71
marsouf
  • 1,005
  • 6
  • 14
  • 1
    Why won't you use `Predicate`? This is polymorphic in `Asset` and then will accept any subtype of `Asset`, isn't it what you want? – Jean-Baptiste Yunès Aug 01 '17 at 16:36
  • yes i know @Jean-BaptisteYunès but i want to stay flexible with my types as possible – marsouf Aug 01 '17 at 16:38
  • 2
    Then read https://stackoverflow.com/questions/4343202/difference-between-super-t-and-extends-t-in-java – Jean-Baptiste Yunès Aug 01 '17 at 16:44
  • @Jean-BaptisteYunès can you explain to me the reasoning behind the comment i posted to the answer below or i'm going wrong with it ? – marsouf Aug 01 '17 at 16:45
  • @Jean-BaptisteYunès the link you pointed me to seems logical to me, but i still can't wrap my head around the error i'm getting in this example. – marsouf Aug 01 '17 at 16:49
  • 2
    From the link, you can't *add* nothing to the predicate (a predicate has a method `test` that *consume* an argument, this is similar to a method add of a collection, you *inject* something to the predicate). So you need to use `super` to be able to *inject*. – Jean-Baptiste Yunès Aug 01 '17 at 16:58

2 Answers2

2

According to Stream#filter

/**
 * Returns a stream consisting of the elements of this stream that match
 * the given predicate.
 *
 * <p>This is an <a href="package-summary.html#StreamOps">intermediate
 * operation</a>.
 *
 * @param predicate a <a href="package-summary.html#NonInterference">non-interfering</a>,
 *                  <a href="package-summary.html#Statelessness">stateless</a>
 *                  predicate to apply to each element to determine if it
 *                  should be included
 * @return the new stream
 */
Stream<T> filter(Predicate<? super T> predicate);

so it should be

 Predicate<? super Asset> isBondAsset

One of the main reasons for that, if I am not wrong, is possibility of using Predicates already defined for <T> super types:

List<String> stringList = Arrays.asList("1","22","3","44");

Predicate<Object> objectPredicate = o -> o.getClass().equals(String.class);

Predicate<CharSequence>  charSequencePredicate = ch -> ch.length() == 2;

Predicate<String> stringPredicate = s -> s.contains("2");

stringList.stream().filter(objectPredicate).forEach(System.out::println);

stringList.stream().filter(charSequencePredicate).forEach(System.out::println);

stringList.stream().filter(stringPredicate).forEach(System.out::println);
Anton Balaniuc
  • 8,462
  • 1
  • 30
  • 47
  • a Stream 's filter method has an argument of type Predicate super T>, a Stream extends T>'s filter method have an argument of type Predicate super ? extends T>, and an argument of type Predicate extends Asset> is a Predicate super ? extends Asset>, cause can be a substitute in the context of super T> – marsouf Aug 01 '17 at 16:42
2

Let's give names to the two type variables:

Collection<T> assets;
Predicate<R> isBondAsset;

Compiler knows that both T and R are subtypes of Asset but that's all it has to go on. In order for Predicate<R> to be applicable to Collection<T> R must be a supertype of T, but you never declare it as such.

There is no reason for you to declare isBondAsset with a wildcard. You should simply make it a Predicate<Asset>. Then the code will compile because the compiler knows that Asset is a supertype of any type that satisfies ? extends Asset

Misha
  • 25,007
  • 4
  • 52
  • 71
  • Let R be a type that is equivalent to extends T>, a Stream 's filter method must be of type Predicate super R>. In my case i gave the filter method the type Predicate which is valid in the context of Predicate super R> – marsouf Aug 01 '17 at 18:11
  • 1
    Your stream is parametrized by `T`, which `extends Asset`. Your predicate is parametrized by `R` which also `extends Asset`. It is NOT the case that `T extends R` or `T super R`. – Misha Aug 01 '17 at 18:16
  • 1
    This is a common source of confusion. When ` extends Asset>` appears twice in the same method, compiler will not assume that they refer to the same type. – Misha Aug 01 '17 at 18:20
  • I totally grasped your answer, i changed my predicate isBondAsset to Predicate super Asset> as extends Asset> is always valid in the context of super Asset> – marsouf Aug 01 '17 at 18:36
  • 1
    Correct. A type that satisfies ` super Asset>` is a supertype of any type that satisfies ` extends Asset>` and so the compiler will let you do this. But you really should consider just making it a `Predicate`. Wildcards are mainly meant for method parameters. Using it like this for a local variable is a good way to cause confusion. – Misha Aug 01 '17 at 18:49