我正在准备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
方法,实际上是一个字符串。
为什么会这样?请给我解释一下。
答案 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> pred
或Predicate<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
变量可以在运行时引用pred
或Predicate<Object>
,并且两者都只能接受Predicate<String>
。
答案 1 :(得分:4)
您似乎对Predicate
对象实例的类型(由lambda创建)与pred
类型(对该对象的引用)之间的区别感到困惑。
由lambda创建的Predicate
实例的类型为Predicate<String>
。
对对象的引用pred
的类型为Predicate<? super String>
,因此可以为类型Predicate<Object>
和Predicate<String>
分配值。但是谓词的test
方法只能与String
个对象一起调用!
这就是? super String
范围的保证。
对象实例始终具有某种具体类型的类型参数,例如Object
或String
。只有对的引用才能具有通配符类型的类型参数。