Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 lower bounded wildcard

I'm preparing for OCP certificate, and I came across with the idea of a lower bounded wildcard. If I understand it correctly, the lower bounded wildcard is used when we want to let Java know the "bounded type" can always be added to our generic Collection.

For example:

public static void addInteger(List<? super Integer> list, Integer i)
{
    list.add(i);
}


public static void main(String[] args)
{   
    List<Number> list = new ArrayList<>();
    addInteger(list, 100);
    addInteger(list, 200);
    System.out.println(list);       // [100,200]

}

Since "? super Integer" indicates that the type must be an Integer or its superclass, adding an Integer to the list will work in each case.

However, this code still compiles and runs like normal:

public static void main(String[] args)
{   
    Predicate<? super String> pred = s -> s.startsWith("M"); // still compiles
    System.out.println(pred.test("Mon")); // Output true

}

Now we have a Predicate that will take 1 parameter which is a String or its super class, but we're not sure it's actually a String or not (What if it's just an Object?). However, we can still access startsWith() method like s is actually a String.

Why does this happen? Please explain to me.

like image 977
Tran Anh Minh Avatar asked Dec 17 '22 16:12

Tran Anh Minh


2 Answers

Predicate<? super String> pred can be assigned either a Predicate<String> or a Predicate<Object>. You are assigning to it a Predicate<String>, which is allowed. The compiler infers that s -> s.startsWith("M") is a Predicate<String> since you are using a String method in the lambda expression.

For example, the following will also pass compilation:

Predicate<? super String> pred = (Object o) -> o.hashCode() > 0;

You can also see that the following passes compilation:

Predicate<String> preds = s -> s.startsWith("M");
Predicate<Object> predo = (Object o) -> o.hashCode() > 0;
Predicate<? super String> pred = preds;
pred = predo;

i.e. Predicate<? super String> can be assigned both a Predicate<String> and a Predicate<Object>.

That said, note that pred.test() will only accept Strings, and not any Object. The reason is that the pred variable can reference either a Predicate<Object> or Predicate<String> at runtime, and only a String is acceptable by both.

like image 166
Eran Avatar answered Jan 01 '23 15:01

Eran


Your seem to be confused about the difference between the type of the Predicate object instance (that is created by the lambda) and type of pred, the reference to that object.

  • The Predicate instance that is created by the lambda has type Predicate<String>.

  • The reference pred to the object is of type Predicate<? super String>, and can thus be assigned values of both type Predicate<Object> and Predicate<String>. But the test method of the predicate can only be called with String objects!

    That's what the ? super String bound ensures.

Object instances always have type parameters of some concrete type, like Object or String. Only references to can have type parameters of wildcard types.

like image 20
Lii Avatar answered Jan 01 '23 15:01

Lii