为什么谓词 <? super SomeClass> 不适用于对象?
Why is Predicate<? super SomeClass> not applicable to Object?
假设我们有一个声明为 Predicate<? super SomeClass>
的谓词。我天真地期望它适用于层次结构中 SomeClass
的任何超类,包括 Object
.
但是这个谓词不适用于Object
。我收到以下错误:
The method test(capture#3-of ? super SomeClass) in the type Predicate is not applicable for the arguments (Object)
Demo.
为什么 Predicate<? super SomeClass>
不适用于 Object
的实例?
代码:
import java.util.*;
import java.lang.*;
import java.io.*;
import java.net.URL;
import java.util.function.Predicate;
/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
public static void main (String[] args) throws java.lang.Exception
{
Predicate<? super URL> p = u -> u.getFile().isEmpty();
p.test(new Object());
}
}
谓词已经有一个类型。谓词接受的类型无法在您尝试调用它时确定。它有一个类型,这个类型是 X 的某个超类;只是未知。
如果我有一个 Predicate<Collection>
,它可以被引用为一个 Predicate<? super List>
。但这并不意味着它会接受任何 Object
。 ? extends X
泛型并不意味着它会接受 任何 匹配给定约束的东西。这意味着它是某种与给定约束匹配的未知类型的谓词。
Predicate<? super List> predicate = Collection::isEmpty;
predicate.test(new Object()); // Should this be valid? Clearly not
对于 Predicate<? super SomeClass>
变量,您可以分配 Predicate<SomeClass>
个实例或 Predicate<Object>
个实例。
但是,您不能将 Object
传递给 Predicate<SomeClass>
的 test()
方法。您只能传递一个 SomeClass
实例。
因此您不能将 Object
传递给 Predicate<? super SomeClass>
的 test()
方法
考虑以下几点:
Predicate<URL> p1 = u -> u.getFile().isEmpty();
Predicate<? super URL> p2 = p1;
p2
指的是 Predicate<URL>
,因此您不能将 new Object()
传递给它的 test()
方法。
换句话说,为了让p.test(new Object())
被编译器接受,它必须对任何 Predicate
有效,可以赋值给Predicate<? super URL> p
变量。由于 Predicate<URL>
Predicate
可以分配给该变量,并且它的 test()
方法 不能 接受 Object
,p.test(new Object())
不能被编译器接受。
顺便说一句,在您的具体示例中,您正在创建一个 Predicate<URL>
,而 URL
是最终的 class。因此,您应该简单地将其声明为:
Predicate<URL> p = u -> u.getFile().isEmpty();
没有理由 ? super
或 ? extends
。
考虑更广泛的例子:
Predicate<? super Integer> p;
在这种情况下,以下任何一个赋值都是有效的,对吗?
p = (Predicate<Integer>) i -> true; // but, only Integer can be applied
p = (Predicate<Number>) n -> true; // Number and its sub-classes (Integer, Double...)
p = (Predicate<Object>) o -> true; // any type
所以,如果最后是:
Predicate<? super Integer> p = (Predicate<Integer>) i -> true;
那么,当然,Number
和Object
都不能应用于谓词。
为了保证类型安全,编译器只允许那些对 Predicate<? super Integer>
的任何可能赋值有效的类型 - 因此,在此特定示例中仅 Integer
。
将下限从 Integer
更改为 Number
分别扩展边界:
Predicate<? super Number> p = (Predicate<Number>) n -> true;
现在,谓词可以应用于 Number
及其任何子 类 :Integer
、Double
等
总结
唯一可以在不破坏类型安全保证的情况下应用于 Predicate<? super SomeClass>
的类型:下限本身及其子类。
java 教程页面提供了一些有关 upper / lower 使用泛型时有界通配符的有用信息。
它还提供了一些 guidelines 来使用通配符,这可以帮助您决定应该使用哪个通配符:
An "In" Variable
An "in" variable serves up data to the code. Imagine a copy method with two arguments: copy(src, dest). The src argument provides the
data to be copied, so it is the "in" parameter. An "Out" Variable
An "out" variable holds data for use elsewhere. In the copy example, copy(src, dest), the dest argument accepts data, so it is the
"out" parameter.
Of course, some variables are used both for "in" and "out" purposes —
this scenario is also addressed in the guidelines.
You can use the "in" and "out" principle when deciding whether to use
a wildcard and what type of wildcard is appropriate. The following
list provides the guidelines to follow: Wildcard Guidelines:
An "in" variable is defined with an upper bounded wildcard, using the extends keyword.
An "out" variable is defined with a lower bounded wildcard, using the super keyword.
In the case where the "in" variable can be accessed using methods defined in the Object class, use an unbounded wildcard.
In the case where the code needs to access the variable as both an "in" and an "out" variable, do not use a wildcard.
These guidelines do not apply to a method's return type. Using a
wildcard as a return type should be avoided because it forces
programmers using the code to deal with wildcards.
这是一个相当复杂的主题。这些 ? extends T
和 ? super T
声明应该在 class 之间创建一个 "matching"。
如果 class 定义了一个接受 Consumer
的方法,比如说 Iterable<T>.foreach()
,它定义了这个消费者接受所有可以接受 T
的东西。因此,它 defines it as forEach(Consumer<? super T> action)
。为什么?因为 Iterator<Integer>
的 foreach()
可以用 Consumer<Number>
甚至 Consumer<Object>
调用。如果没有 ? super T
.
,这是不可能的
OTOH,如果 class 定义了采用 Supplier
的方法,比如 addFrom
,它定义该供应商提供 T
的所有内容。因此,它将其定义为 addFrom(Supplier<? extends T> supplier)
。为什么?因为 ThisClass<Number>
的 addFrom()
可以用 Supplier<Integer>
或 Supplier<Double>
调用。如果没有 ? extends T
.
,这是不可能的
对于后者来说也许是一个更好的例子:List<E>.addAll()
需要一个 Collection<? extends E>
。这使得可以将 List<Integer>
的元素添加到 List<Number>
,但反之则不行。
假设我们有一个声明为 Predicate<? super SomeClass>
的谓词。我天真地期望它适用于层次结构中 SomeClass
的任何超类,包括 Object
.
但是这个谓词不适用于Object
。我收到以下错误:
The method test(capture#3-of ? super SomeClass) in the type Predicate is not applicable for the arguments (Object)
Demo.
为什么 Predicate<? super SomeClass>
不适用于 Object
的实例?
代码:
import java.util.*;
import java.lang.*;
import java.io.*;
import java.net.URL;
import java.util.function.Predicate;
/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
public static void main (String[] args) throws java.lang.Exception
{
Predicate<? super URL> p = u -> u.getFile().isEmpty();
p.test(new Object());
}
}
谓词已经有一个类型。谓词接受的类型无法在您尝试调用它时确定。它有一个类型,这个类型是 X 的某个超类;只是未知。
如果我有一个 Predicate<Collection>
,它可以被引用为一个 Predicate<? super List>
。但这并不意味着它会接受任何 Object
。 ? extends X
泛型并不意味着它会接受 任何 匹配给定约束的东西。这意味着它是某种与给定约束匹配的未知类型的谓词。
Predicate<? super List> predicate = Collection::isEmpty;
predicate.test(new Object()); // Should this be valid? Clearly not
对于 Predicate<? super SomeClass>
变量,您可以分配 Predicate<SomeClass>
个实例或 Predicate<Object>
个实例。
但是,您不能将 Object
传递给 Predicate<SomeClass>
的 test()
方法。您只能传递一个 SomeClass
实例。
因此您不能将 Object
传递给 Predicate<? super SomeClass>
test()
方法
考虑以下几点:
Predicate<URL> p1 = u -> u.getFile().isEmpty();
Predicate<? super URL> p2 = p1;
p2
指的是 Predicate<URL>
,因此您不能将 new Object()
传递给它的 test()
方法。
换句话说,为了让p.test(new Object())
被编译器接受,它必须对任何 Predicate
有效,可以赋值给Predicate<? super URL> p
变量。由于 Predicate<URL>
Predicate
可以分配给该变量,并且它的 test()
方法 不能 接受 Object
,p.test(new Object())
不能被编译器接受。
顺便说一句,在您的具体示例中,您正在创建一个 Predicate<URL>
,而 URL
是最终的 class。因此,您应该简单地将其声明为:
Predicate<URL> p = u -> u.getFile().isEmpty();
没有理由 ? super
或 ? extends
。
考虑更广泛的例子:
Predicate<? super Integer> p;
在这种情况下,以下任何一个赋值都是有效的,对吗?
p = (Predicate<Integer>) i -> true; // but, only Integer can be applied
p = (Predicate<Number>) n -> true; // Number and its sub-classes (Integer, Double...)
p = (Predicate<Object>) o -> true; // any type
所以,如果最后是:
Predicate<? super Integer> p = (Predicate<Integer>) i -> true;
那么,当然,Number
和Object
都不能应用于谓词。
为了保证类型安全,编译器只允许那些对 Predicate<? super Integer>
的任何可能赋值有效的类型 - 因此,在此特定示例中仅 Integer
。
将下限从 Integer
更改为 Number
分别扩展边界:
Predicate<? super Number> p = (Predicate<Number>) n -> true;
现在,谓词可以应用于 Number
及其任何子 类 :Integer
、Double
等
总结
唯一可以在不破坏类型安全保证的情况下应用于 Predicate<? super SomeClass>
的类型:下限本身及其子类。
java 教程页面提供了一些有关 upper / lower 使用泛型时有界通配符的有用信息。
它还提供了一些 guidelines 来使用通配符,这可以帮助您决定应该使用哪个通配符:
An "In" Variable An "in" variable serves up data to the code. Imagine a copy method with two arguments: copy(src, dest). The src argument provides the data to be copied, so it is the "in" parameter. An "Out" Variable An "out" variable holds data for use elsewhere. In the copy example, copy(src, dest), the dest argument accepts data, so it is the "out" parameter.
Of course, some variables are used both for "in" and "out" purposes — this scenario is also addressed in the guidelines.
You can use the "in" and "out" principle when deciding whether to use a wildcard and what type of wildcard is appropriate. The following list provides the guidelines to follow: Wildcard Guidelines:
An "in" variable is defined with an upper bounded wildcard, using the extends keyword. An "out" variable is defined with a lower bounded wildcard, using the super keyword. In the case where the "in" variable can be accessed using methods defined in the Object class, use an unbounded wildcard. In the case where the code needs to access the variable as both an "in" and an "out" variable, do not use a wildcard.
These guidelines do not apply to a method's return type. Using a wildcard as a return type should be avoided because it forces programmers using the code to deal with wildcards.
这是一个相当复杂的主题。这些 ? extends T
和 ? super T
声明应该在 class 之间创建一个 "matching"。
如果 class 定义了一个接受 Consumer
的方法,比如说 Iterable<T>.foreach()
,它定义了这个消费者接受所有可以接受 T
的东西。因此,它 defines it as forEach(Consumer<? super T> action)
。为什么?因为 Iterator<Integer>
的 foreach()
可以用 Consumer<Number>
甚至 Consumer<Object>
调用。如果没有 ? super T
.
OTOH,如果 class 定义了采用 Supplier
的方法,比如 addFrom
,它定义该供应商提供 T
的所有内容。因此,它将其定义为 addFrom(Supplier<? extends T> supplier)
。为什么?因为 ThisClass<Number>
的 addFrom()
可以用 Supplier<Integer>
或 Supplier<Double>
调用。如果没有 ? extends T
.
对于后者来说也许是一个更好的例子:List<E>.addAll()
需要一个 Collection<? extends E>
。这使得可以将 List<Integer>
的元素添加到 List<Number>
,但反之则不行。