Java 8下界通配符

时间:2019-06-19 06:26:36

标签: java generics java-8

我正在准备OCP证书,并且遇到了下界通配符的想法。如果我对它的理解正确,那么当我们想让Java知道“绑定类型”总是可以添加到我们的通用Collection中时,就会使用下界通配符。

例如:

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]

}

由于“?super Integer”表示类型必须是Integer或其父类,因此在每种情况下都可以将Integer添加到列表中。

但是,此代码仍然可以正常编译和运行:

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

}

现在,我们有一个谓词,它将采用1个参数,该参数是String或其父类,但是我们不确定它实际上是否是String(如果它只是一个对象呢?)。但是,我们仍然可以访问startsWith()之类的s方法,实际上是一个字符串。

为什么会这样?请给我解释一下。

2 个答案:

答案 0 :(得分:7)

可以为

#!/bin/sh # udhcpc script edited by Tim Riker <Tim@Rikers.org> RESOLV_CONF="/etc/resolv.conf" [ -n "$1" ] || { echo "Error: should be called from udhcpc"; exit 1; } NETMASK="" [ -n "$subnet" ] && NETMASK="netmask $subnet" BROADCAST="broadcast +" [ -n "$broadcast" ] && BROADCAST="broadcast $broadcast" case "$1" in deconfig) echo "Setting IP address 0.0.0.0 on $interface" ifconfig $interface 0.0.0.0 ;; renew|bound) echo "Setting IP address $ip on $interface" ifconfig $interface $ip $NETMASK $BROADCAST if [ -n "$router" ] ; then echo "Deleting routers" while route del default gw 0.0.0.0 dev $interface ; do : done metric=0 for i in $router ; do echo "Adding router $i" route add default gw $i dev $interface metric $((metric++)) done fi echo "Recreating $RESOLV_CONF" echo -n > $RESOLV_CONF-$$ [ -n "$domain" ] && echo "search $domain" >> $RESOLV_CONF-$$ for i in $dns ; do echo " Adding DNS server $i" echo "nameserver $i" >> $RESOLV_CONF-$$ done mv $RESOLV_CONF-$$ $RESOLV_CONF ;; esac exit 0 分配Predicate<? super String> predPredicate<String>。您正在为其分配一个Predicate<Object>,这是允许的。编译器推断Predicate<String>s -> s.startsWith("M"),因为您在lambda表达式中使用了Predicate<String>方法。

例如,以下代码也将通过编译:

String

您还可以看到以下代码通过了编译:

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

即可以同时为Predicate<String> preds = s -> s.startsWith("M"); Predicate<Object> predo = (Object o) -> o.hashCode() > 0; Predicate<? super String> pred = preds; pred = predo; Predicate<? super String>分配Predicate<String>

也就是说,请注意Predicate<Object>将仅接受pred.test(),而不接受任何String。原因是Object变量可以在运行时引用predPredicate<Object>,并且两者都只能接受Predicate<String>

答案 1 :(得分:4)

您似乎对Predicate对象实例的类型(由lambda创建)与pred类型(对该对象的引用)之间的区别感到困惑。

  • 由lambda创建的Predicate实例的类型为Predicate<String>

  • 对对象的引用pred的类型为Predicate<? super String>,因此可以为类型Predicate<Object>Predicate<String>分配值。但是谓词的test方法只能与String个对象一起调用!

    这就是? super String范围的保证。

对象实例始终具有某种具体类型的类型参数,例如ObjectString。只有对的引用才能具有通配符类型的类型参数。