为什么这段代码有效
ArrayList<?>[] arr = new ArrayList<?>[2];
Run Code Online (Sandbox Code Playgroud)
但以下两个不是?
ArrayList<? extends Object>[] arr = new ArrayList<? extends Object>[2];
ArrayList<? super Object>[] arr = new ArrayList<? super Object>[2];
Run Code Online (Sandbox Code Playgroud)
最后两行生成编译错误;
错误:通用数组创建.
请澄清差异.
另一方面ArrayList<?>[] arr = new ArrayList<?>[2];编译好但是
ArrayList<?> arr = new ArrayList<?>();
Run Code Online (Sandbox Code Playgroud)
不.
Class<? extends Integer>将编译正常,但是Integer是最终类型,因此将它用作上限是没有意义的(没有任何东西会永远extend).
如果您尝试使用final类型作为类型参数的上限,您将收到编译器警告:
类型参数T不应受最终类型Integer的限制.最终类型无法进一步扩展
为什么使用final类型作为通配符的上限是完全正常的,但是为类型参数抛出警告?为什么Java甚至允许通配符被最终的上层类型限制?
假设我有以下课程:
public class Either<A, B> {
public Object get();
}
Run Code Online (Sandbox Code Playgroud)
Either是一种存储A或B类型的对象的类型. get()检索该对象.
现在的问题是,是否有可能使用泛型改变的方法签名get(),以便返回的类型不只是Object,但A和B的公用超例如,Either<Integer, Long>可以有get()回报Number,一个Either<Deque<T>, Set<T>>能有get()回报Iterable<T>或者Collection<T>,等等.(显然,Either<Foo,Foo>应该有get()回报Foo).
如果这是可能的话,如果我有Either<List<A>, List<B>>,那么最具体的类型get()可以返回什么?它是原始的List,通配符List<?>还是完全不同的东西?
为什么实用工厂方法经常使用特定的通用参数(如T)而不是有界通配符参数(如? super T)?
例如,Functions#forPredicate的签名是:
public static <T> Function<T, Boolean> forPredicate(Predicate<T> predicate)
Run Code Online (Sandbox Code Playgroud)
为什么不使用:
public static <T> Function<T, Boolean> forPredicate(Predicate<? super T> predicate)
Run Code Online (Sandbox Code Playgroud)
哪个会产生如下可能的东西?
Predicate<Number> isPositivePredicate = ...
Function<Integer, Boolean> isPositiveInteger = Functions.forPredicate(isPositivePredicate);
// above line is compiler error:
// Type mismatch: cannot convert from Function<Number,Boolean> to Function<Integer,Boolean>
Run Code Online (Sandbox Code Playgroud)
是不是因为消费者的Function,并Predicate预计将有必要界通配符参数,使这种不必要的?例如,Iterables #ref的泛型边界允许Predicate<Number>在以下情况下使用a Iterable<Integer>:
public static <T> T find(Iterable<T> iterable,
Predicate<? super T> predicate)
Run Code Online (Sandbox Code Playgroud)
还有其他原因吗?
假设我在Java中有这个:
List<String> list = new ArrayList<String>();
list.getClass();
Run Code Online (Sandbox Code Playgroud)
最后一个表达式的类型是Class<? extends List>.我理解为什么,由于擦除,它不可能Class<? extends List<String>>.但为什么不能呢Class<? extends List<?>>?
如果我想将这个表达式的结果分配给一个变量,以某种方式保存这个类实际上是某种类型的信息,我是否无法避免未经检查的强制转换警告和原始类型警告List?
Class<? extends List> listClass = list.getClass(); // raw type warning
Class<? extends List<?>> listClass = (Class<? extends List<?>>) list.getClass(); // unchecked cast warning
Run Code Online (Sandbox Code Playgroud) 以下在JDK8中编译得很好,但是与JDK7 给出了不兼容的类型错误.
List<List<? extends Number>> xs = Arrays.asList(Arrays.asList(0));
Run Code Online (Sandbox Code Playgroud)
根据这个答案,List<List<? extends Number>>没有超类型的关系List<List<Integer>>.
在Java 8中改变了什么使得这个任务有效?我也有一个很难理解为什么它不会在Java 7的工作.
这两个语句使用JDK7编译时没有类型错误:
List<? extends Number> xs = Arrays.asList(0);
List<? extends List<? extends Number>> ys = Arrays.asList(Arrays.asList(0));
Run Code Online (Sandbox Code Playgroud)
对我来说,这两个都在JDK7中工作似乎非常不直观,但上面的原始示例却没有.所有这些当然都适用于JDK8.我想要真正理解这里发生了什么,我需要理解为什么这些例子在Java 7中是合法的,但原始的例子却不是.
有人可以向我解释为什么以下代码不起作用?
public class Test {
interface Strategy<T> {
void execute(T t);
}
public static class DefaultStrategy<T> implements Strategy<T> {
@Override
public void execute(T t) {}
}
public static class Client {
private Strategy<?> a;
public void setStrategy(Strategy<?> a) {
this.a = a;
}
private void run() {
a.execute("hello world");
}
}
public static void main(String[] args) {
Client client = new Client();
client.setStrategy(new DefaultStrategy<String>());
client.run();
}
}
Run Code Online (Sandbox Code Playgroud)
我收到以下错误:
The method execute(capture#3-of ?) in the type Test.Strategy<capture#3-of ?>
is not applicable …Run Code Online (Sandbox Code Playgroud) 通常不鼓励在Java中的返回参数中使用通用通配符类型.例如,Effective Java,第28项规定:
不要将通配符类型用作返回类型.它不会为您的用户提供额外的灵活性,而是迫使他们在客户端代码中使用通配符类型.
正确使用的通配符类型对于类的用户几乎是不可见的.它们使方法接受它们应该接受的参数并拒绝它们应该拒绝的参数.如果类的用户必须考虑通配符类型,那么类的API可能有问题.
但在某些情况下,它似乎是最佳选择,例如在下面的代码中:
import java.util.*;
import java.lang.*;
import java.io.*;
class Container
{
private final Map<String, Type<?>> itemMap = new HashMap<>();
public <T> void addItem(String key, T t) {
itemMap.put(key, new Type<>(t));
}
public Collection<Type<?>> getAllItems() {
return itemMap.values();
}
public static class Type<T> {
private final T value;
public Type(T value) {
this.value = value;
}
@Override
public String toString() {
return "Type(" + value + ")";
}
}
public static void main (String[] args)
{ …Run Code Online (Sandbox Code Playgroud) 对于Comparator类中的比较源代码
public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
Function<? super T, ? extends U> keyExtractor)
{
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable) (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}
Run Code Online (Sandbox Code Playgroud)
我明白之间的差别super和extends.我不明白的是,为什么这种方法有它们.有人能举例说明参数看起来像什么时无法实现的Function<T, U> keyExtractor?
例如 :
Comparator<Employee> employeeNameComparator = Comparator.comparing(Employee::getName);
Run Code Online (Sandbox Code Playgroud)
也可以使用以下函数定义进行编译
public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
Function<T, U> keyExtractor)
{
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable) (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}
Run Code Online (Sandbox Code Playgroud) 大多数关于通配符的问题都想知道为什么编译器会拒绝一些合理的东西。我的问题是相反的。为什么下面的程序会被编译器接受?
void test(List<? extends Number> g1, List<? extends Number> g2)
{
g1 = g2;
}
Run Code Online (Sandbox Code Playgroud)
我试图从 Java 语言规范中解释这一点,但我还没有找到答案。我从 Java 泛型和通配符的各种描述中得到的印象是,通配符的每次使用都被捕获为一种全新的类型,但显然不是在这里。我没有发现允许此分配后出现任何令人讨厌的行为,但它似乎仍然是“错误的”。
bounded-wildcard ×10
generics ×10
java ×10
wildcard ×2
arrays ×1
guava ×1
java-8 ×1
reflection ×1
types ×1